题意:
给出一个n个结点m条边的带权无向连通图,有q次操作;
每次操作是修改一个边的权值,要求每次操作后输出这个图中最小生成树的权值和;
n<=20000,m<=50000,q<=50000;
题解:
网上题解都是那些鬼畜的分治做法,每层求最小生成树将问题缩小到可以接受的范围;
不过那个方法不好理解并且难以推广,所以wyfcyx大爷提出了一种更加让人愉悦的做法;
首先这个问题不能直接用LCT维护,因为当删去一条边(边的权值变大)之后,我们无法知道这两个连通块之间是否还有更小边相连;
也就是说,LCT维护最小生成树不能删边,所以利用分治做一个转化;
我们考虑一个时间轴,每一条边作为一个区间覆盖了它存在的时间段;
那么对于一个询问的时间点,当时在这个图上的边即为覆盖在点上面的那些;
所以用线段树的姿势,将每个边的区间拆成不超过log个,然后挂在那个线段树结点上;
现在从根到某个线段树的叶子,将所有经过结点挂的边一一加入LCT跑最小生成树,跑到最下面就是对那个时间点的答案;
而因为要查询全部,那就应当在深搜回溯的时候,删除刚才插入的边,然后返回父结点,再向另一个子结点搜索;
但是LCT并不能支持删边啊。。。也并不支持向左右子树各自可持久化的分叉出一个版本。。。
然而wyfcyx大爷出现了!因为LCT的操作是十分简单的,只有Link和Cut,并且恰为相反操作;
因此记录每次加入时弹掉的边和加入的边,回溯的时候直接逆操作恢复LCT即可,这样复杂度仍然是有保障的;
虽说如此是有保障的,但是实际操作上非常卡!这个算法相当于每一条边加了两次删了两次,再算上LCT的大常数,所以不太好过嘛;
卡卡常数,时间复杂度O(nlog^2n)就可以在BZ过啦;
最终答案要开long long;
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 71000 #define M 51000 #define MEM 800000 #define LEN 1<<15 #define lson l,mid,no<<1 #define rson mid+1,r,no<<1|1 #define which(x) (ch[fa[x]][1]==x) using namespace std; typedef long long ll; struct edge { int x,y,val,no; }E[M],list[MEM]; ll ans[M],tot; int last[M]; int head[M<<2],next[MEM],ce; int fa[N],ch[N][2],val[N],ma[N],n,m; int st[MEM],stv[MEM],top; bool opt[MEM]; bool rev[N],rt[N]; inline char getc() { static char *S,*T,buf[LEN]; if(S==T) { T=(S=buf)+fread(buf,1,LEN,stdin); if(S==T) return EOF; } return *S++; } inline bool isdigit(char x) { return '0'<=x&&x<='9'; } inline int read() { static char ch; static int D; while(!isdigit(ch=getc())); for(D=ch-'0';isdigit(ch=getc());) D=D*10+ch-'0'; return D; } inline void Pushup(int x) { ma[x]=val[ma[ch[x][0]]]>val[ma[ch[x][1]]]?ma[ch[x][0]]:ma[ch[x][1]]; ma[x]=val[ma[x]]>val[x]?ma[x]:x; } inline void Pushdown(int x) { if(rev[x]) { swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; rev[x]=0; } } inline void down(int x) { if(!rt[x]) down(fa[x]); Pushdown(x); } inline void Rotate(int x) { int f=fa[x]; bool k=which(x); ch[f][k]=ch[x][!k]; ch[x][!k]=f; if(rt[f]) rt[f]^=rt[x]^=1; else ch[fa[f]][which(f)]=x; fa[ch[f][k]]=f; fa[x]=fa[f]; fa[f]=x; Pushup(f); // Pushup(x); } inline void Splay(int x) { down(x); while(!rt[x]) { int f=fa[x]; if(rt[f]) { Rotate(x); break; } if(which(x)^which(f)) Rotate(x); else Rotate(f); Rotate(x); } Pushup(x); } inline void access(int x) { int y=0; while(x) { Splay(x); rt[ch[x][1]]=1; ch[x][1]=y; rt[y]=0; Pushup(x); y=x,x=fa[x]; } } inline void Mtr(int x) { access(x); Splay(x); swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; } inline bool judge(int x,int y) { Mtr(x); access(y); Splay(x); while(ch[x][1]) x=ch[x][1]; return x==y; } inline void Cut(int x) { Mtr(x); static int t; t=E[x-n].x; access(t); Splay(x); rt[t]=1,fa[t]=0; ch[x][1]=0; Pushup(x); t=E[x-n].y; access(t); Splay(x); rt[t]=1,fa[t]=0; ch[x][1]=0; Pushup(x); } inline void Link(int x) { Mtr(x); fa[x]=E[x-n].x; Mtr(x); fa[x]=E[x-n].y; } inline void add(int no,edge &x) { list[++ce]=x; next[ce]=head[no]; head[no]=ce; } inline void update(int l,int r,int no,int st,int en,edge &x) { if(st<=l&&r<=en) add(no,x); else { int mid=l+r>>1; if(en<=mid) update(lson,st,en,x); else if(st>mid) update(rson,st,en,x); else update(lson,st,en,x),update(rson,st,en,x); } } inline void slove(int l,int r,int no) { int cnt,i,temp; for(i=head[no],cnt=0;i;i=next[i]) { temp=0; if(!judge(list[i].x,list[i].y)||val[temp=ma[list[i].x]]>=list[i].val) { if(temp) { cnt++,top++; st[top]=temp; stv[top]=val[temp]; opt[top]=0; Cut(temp); tot-=val[temp]; } cnt++,top++; st[top]=list[i].no; opt[top]=1; val[list[i].no]=list[i].val; Link(list[i].no); tot+=list[i].val; } } if(l==r) { ans[l]=tot; } else { int mid=l+r>>1; slove(lson); slove(rson); } while(cnt) { if(opt[top]==0) { val[st[top]]=stv[top]; Link(st[top]); tot+=val[st[top]]; } else { Cut(st[top]); tot-=val[st[top]]; } top--; cnt--; } } int main() { int q,i,j,k,x,y,v; n=read(),m=read(),q=read(); for(i=1;i<=m;i++) { E[i].x=read(),E[i].y=read(),E[i].val=read(); last[i]=1; E[i].no=i+n; } for(i=1;i<=q;i++) { x=read(),y=read(); if(last[x]<=i-1) update(1,q,1,last[x],i-1,E[x]); last[x]=i,E[x].val=y; } for(i=1;i<=m;i++) { update(1,q,1,last[i],q,E[i]); } for(i=1;i<=n+m;i++) { rt[i]=1; ma[i]=i; } slove(1,q,1); for(i=1;i<=q;i++) { printf("%lld\n",ans[i]); } return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/49779217