玄学cdq
O(nlog^2n)的动态最小生成树
其实就是按照时间cdq分治+剪枝(剪掉一定出现和不可能出现的边)
处理[l,r]之间的修改以及修改之后的询问,不能确定是否加入的边集为E
对于会被改变边权的边,边集为Q,暂时不能确定
不妨大力假设:
都是-inf,这个时候把Q的边都加入之后,剩下的E进行kruskal如果还能加入,那么在[l,r]这个区间里的所有询问,一定都能加进去
并查集带着必须边,然后处理Q都是inf,剩下的E进行kruskal,如果还是不能加入,那么在[l,r]这个区间里的所有询问,一定都不会加进去
这样,Q和第二次能加进去的边继续往左右递归,继续确定。
到了l==r时候,
把这个修改生效(因为之后再考虑的时候边权一定已经变了)
暴力把E中的边进行kruskal即可。
按秩合并并查集撤销,有些必须边出了[l,r]可能就被替换了。
说白了就是,cdq分治,强行维护备选边集+大力剪枝
感性理解一下复杂度:
看做n,m,q同阶
当Q中的边集最分散的时候,也就是形成一棵树,此时能确定的边是最少的。
这样,每次len/2,那么至少会多确定len/2个边(解放了n/2个点)
规模大概/=2
实际应该远不到上界,但是sort常数很大
O(nlog^2n)
注意,并查集不要随手写成路径压缩!!!
// luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Miracle{ const int N=50000+5; int n,m,q; struct edge{ int x,y,w; bool friend operator <(edge a,edge b){ return a.w<b.w; } }E[N]; bool cmp(int x,int y){ return E[x]<E[y]; } struct qs{ int id,w; }Q[N]; ll ans[N]; int fa[N],sz[N]; int exi[N],tag; int fin(int x){return fa[x]==x?x:fin(fa[x]);} vector<pair<int *,int> >buc; void push(int &x,int v){buc.push_back(mk(&x,x));x=v;} void roll(int t){ while((int)buc.size()>t) *buc.back().fi=buc.back().se,buc.pop_back(); } void merge(int x,int y){ x=fin(x);y=fin(y); if(x==y) return; if(sz[x]>sz[y]) swap(x,y); push(fa[x],y);push(sz[y],sz[x]+sz[y]); } void wrk(vector<int> &e,int l,int r,ll &val){ int st=buc.size(); static vector<int>tmp;tmp.clear(); sort(e.begin(),e.end(),cmp); for(reg i=l;i<=r;++i) merge(E[Q[i].id].x,E[Q[i].id].y); for(solid i:e){ if(exi[i]==tag) continue; int x=fin(E[i].x),y=fin(E[i].y); if(x!=y){ merge(x,y);val+=E[i].w;tmp.pb(i); } } roll(st); for(solid i:tmp){ merge(E[i].x,E[i].y); } } void dele(vector<int> &e){ vector<int>tmp; sort(e.begin(),e.end(),cmp); int st=buc.size(); for(solid i:e){ if(exi[i]==tag){ tmp.pb(i);continue; } int x=fin(E[i].x),y=fin(E[i].y); if(x!=y){ merge(x,y);tmp.pb(i); } } roll(st); e.swap(tmp); } void sol(int l,int r,vector<int>e,ll val){ // cout<<" l "<<l<<" r "<<r<<" val "<<val<<endl; // for(solid i:e){ // cout<<i<<" "; // }enter; if(l==r) E[Q[l].id].w=Q[l].w; int st=buc.size(); if(l==r){ sort(e.begin(),e.end(),cmp); for(solid i:e){ int x=fin(E[i].x),y=fin(E[i].y); if(x!=y){ merge(x,y);val+=E[i].w; } } ans[l]=val; }else{ ++tag; for(reg i=l;i<=r;++i) exi[Q[i].id]=tag; wrk(e,l,r,val); dele(e); int mid=(l+r)>>1; sol(l,mid,e,val);sol(mid+1,r,e,val); } roll(st); } int main(){ rd(n);rd(m);rd(q); vector<int>st; for(reg i=1;i<=m;++i){ rd(E[i].x);rd(E[i].y);rd(E[i].w); st.push_back(i); } for(reg i=1;i<=n;++i) fa[i]=i,sz[i]=1; for(reg i=1;i<=q;++i){ rd(Q[i].id);rd(Q[i].w); } sol(1,q,st,0); for(reg i=1;i<=q;++i){ printf("%lld\n",ans[i]); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* */