水管局长
题目链接:https://www.luogu.org/problemnew/show/P4172#sub
LCT
显然两个点的路径上的边最大要最小在该图最小生成树上
正删倒加,倒着做变成加边操作
加边时判断一下是否能形成更优的生成树,用LCT删除和连接操作即可
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int M=2e5+9; 5 int n,m,q; 6 int fr[M],to[M],e[1009][1009],d[1009][1009]; 7 int O[M],X[M],Y[M]; 8 bool vis[1009][1009],rev[M]; 9 int f[M],c[M][2],Ma[M],s[M],ans[M]; 10 bool bol=0; 11 int read(){ 12 int rex=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){rex=rex*10+ch-'0';ch=getchar();} 15 return rex*f; 16 } 17 bool isroot(int x){ 18 return c[f[x]][0]!=x&&c[f[x]][1]!=x; 19 } 20 int Max(int x,int y){ 21 return e[fr[x]][to[x]]<e[fr[y]][to[y]]?y:x; 22 } 23 void pushup(int x){ 24 Ma[x]=Max(Max(Ma[c[x][0]],Ma[c[x][1]]),x); 25 } 26 void pushdown(int x){ 27 if(rev[x]){ 28 swap(c[x][0],c[x][1]); 29 rev[x]^=1,rev[c[x][0]]^=1,rev[c[x][1]]^=1; 30 } 31 } 32 void rotate(int x){ 33 int y=f[x],z=f[y],k=c[y][1]==x,ch=c[x][k^1]; 34 if(!isroot(y))c[z][c[z][1]==y]=x;f[x]=z; 35 c[y][k]=ch;f[ch]=y; 36 c[x][k^1]=y;f[y]=x; 37 pushup(y),pushup(x); 38 39 } 40 void splay(int x){ 41 int top=0,u=x; 42 while(!isroot(u))s[++top]=u,u=f[u];s[++top]=u; 43 while(top)pushdown(s[top--]); 44 for(int y=f[x];!isroot(x);y=f[x]){ 45 if(!isroot(y)) 46 rotate(((c[f[y]][1]==y)==(c[y][1]==x))?y:x); 47 rotate(x); 48 } 49 } 50 void access(int x){for(int t=0;x;t=x,x=f[x]){splay(x);c[x][1]=t;pushup(x);}} 51 void makeroot(int x){access(x),splay(x),rev[x]^=1;} 52 int findroot(int x){access(x);splay(x);while(c[x][0])x=c[x][0];return x;} 53 void split(int x,int y){makeroot(x),access(y),splay(y);} 54 void link(int x,int y){makeroot(x),f[x]=y;} 55 void cut(int x,int y){split(x,y);c[y][0]=0;f[x]=0;pushup(y);} 56 int main(){ 57 n=read(),m=read(),q=read(); 58 for(int i=1,u,v;i<=m;++i){ 59 fr[i]=u=read(),to[i]=v=read(); 60 d[u][v]=d[v][u]=i;Ma[i]=i; 61 e[u][v]=e[v][u]=read(); 62 } 63 for(int i=1;i<=q;++i){ 64 O[i]=read(),X[i]=read(),Y[i]=read(); 65 if(O[i]==2)vis[X[i]][Y[i]]=vis[Y[i]][X[i]]=1; 66 } 67 for(int i=1;i<=m;++i){ 68 int x=fr[i]+m,y=to[i]+m; 69 if(!vis[fr[i]][to[i]]){ 70 if(findroot(x)!=findroot(y)){ 71 link(x,i); 72 link(y,i); 73 } 74 else { 75 split(x,y);int v=Ma[y]; 76 if(Max(v,i)==v){ 77 cut(v,fr[v]+m); 78 cut(v,to[v]+m); 79 link(x,i); 80 link(y,i); 81 } 82 } 83 } 84 } 85 for(int i=q,x,y;i>=1;--i){ 86 x=X[i]+m,y=Y[i]+m;int u=d[X[i]][Y[i]]; 87 if(O[i]==1){ 88 split(x,y);int v=Ma[y]; 89 ans[i]=e[fr[v]][to[v]]; 90 } 91 if(O[i]==2){ 92 if(findroot(x)!=findroot(y)){ 93 link(x,i); 94 link(y,i); 95 } 96 else { 97 split(x,y);int v=Ma[y]; 98 if(Max(v,u)==v){ 99 cut(v,fr[v]+m); 100 cut(v,to[v]+m); 101 link(x,u); 102 link(y,u); 103 } 104 } 105 } 106 } 107 for(int i=1;i<=q;++i){ 108 if(O[i]==1)printf("%d\n",ans[i]); 109 } 110 return 0; 111 }
此题看了题解之后,发现还有一种玄学做法
参考:https://www.luogu.org/blog/xuege/solution-p4172
似乎只用fa数组存最小生成树
查询是暴力在树上一个一个的走
修改就是 把u到 根节点/要删的边的位置 的 fa数组 全部取反(此时u就成为了根节点),令fa[u]=v;(连边),这种方法较快速度(O(n)) 更新了最小生成树
感觉非常暴力,但貌似跑非常快
然后还有一种解法是根据n<=1000和删边次数不超过5000来做的
参考:http://www.docin.com/p-40358260.html
删边次数不多,每次暴力重构一下最小生成树,然后用dfs序+st表找lca(因为可以O(1)查询)
(这个算法正着做可以)
可能时间有点复杂,考虑优化
查询的话,查询O(1) 的在线lca写法;
修改的话,可以采用上面那个用fa数组维护的最小生成树的更新方法,更新时间O(n)(这个时间非常不满);
这样就不用每次重新mlogm求一遍最小生成树了
求出新最小生成树后,有一个预处理O(n),查询O(1)的lca写法;这里执行预处理
O(n)预处理 (均值RMQ https://www.cnblogs.com/intercept/p/10029466.html ),
当然这种修改的话就不能正着做了,需要倒着来
时间O(修改次数*n+查询次数*1)