有一个两部各有 \(n\) 个节点的二分图 \(G\),定义 \(G^m\) 为一个 \(m+1\) 层的图,每层有 \(n\) 个节点,相邻两层的诱导子图都和 \(G\) 相同。dom
给你 \(m\),求对于全部 \(1\leq i\leq m\),\(G^i\) 的最小生成树的边权和。spa
保证图连通。code
\(n,m\leq 100000,\text{边数 }\leq 200000,\text{边权}\leq 30\)get
对于 \(G^i\),先求出用了多少种边权 \(<j\) 的边,再求出用了多少条边权 \(\leq j\) 的边,就能够获得用了多少条边权为 \(j\) 的边。string
那么边权就能够忽略了。it
如今要求出 \(G^i\) 有多少条边。io
从左往右扫,用并查集维护最后两层节点的连通性。function
那么再下一层的并查集确定会是这两层的并查集加上一点边。class
当咱们处理完一层的时候,求出这层新加的边对下一层的贡献。im
这层每加一条边,下一层就要在这两个集合右侧的点之间连边。
而后不停地往右边传就行了。
每加一条边就会合并两个集合,因此总共会加 \(O(n)\) 条边。
时间复杂度:\(O(w(n+m+e)\alpha(n))\)
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<ctime> #include<functional> #include<cmath> #include<vector> #include<assert.h> //using namespace std; using std::min; using std::max; using std::swap; using std::sort; using std::reverse; using std::random_shuffle; using std::lower_bound; using std::upper_bound; using std::unique; using std::vector; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef std::pair<int,int> pii; typedef std::pair<ll,ll> pll; void open(const char *s){ #ifndef ONLINE_JUDGE char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } void open2(const char *s){ #ifdef DEBUG char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;} void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');} int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;} int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;} const int N=200010; int f[N]; vector<pii> g[40],a,b; int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } int c[N]; int merge(int x,int y) { if(find(x)==find(y)) return 0; if(!c[find(y)]) c[find(y)]=c[find(x)]; f[find(x)]=find(y); return 1; } int n,m,e; ll ans[N]; ll s[N]; ll d[N]; int main() { open("c"); scanf("%d%d%d",&n,&m,&e); int x,y,w; for(int i=1;i<=e;i++) { scanf("%d%d%d",&x,&y,&w); g[w].push_back(pii(x,y)); } for(int i=1;i<=30;i++) { a.clear(); for(int j=1;j<=i;j++) for(auto v:g[j]) a.push_back(v); for(int j=1;j<=2*n;j++) f[j]=j; for(int j=1;j<=m;j++) d[j]=0; for(int j=1;j<=2*n;j++) c[j]=0; for(auto v:a) d[1]+=merge(v.first,v.second+n); b.clear(); for(int j=n+1;j<=2*n;j++) if(!c[find(j)]) c[find(j)]=j; else b.push_back(pii(c[find(j)]-n,j-n)); for(int j=2;j<=m;j++) { a=b; b.clear(); for(auto v:a) if(find(v.first)!=find(v.second)) { if(c[find(v.first)]&&c[find(v.second)]) b.push_back(pii(c[find(v.first)]-n,c[find(v.second)]-n)); merge(v.first,v.second); } else d[j]--; } for(int j=2;j<=m;j++) d[j]+=d[j-1]; for(int j=2;j<=m;j++) d[j]+=d[j-1]; for(int j=1;j<=m;j++) { ans[j]+=i*(d[j]-s[j]); s[j]=d[j]; } } for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }