[WC2006]水管局长

水管局长

题目链接: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 }
View Code

 此题看了题解之后,发现还有一种玄学做法

  参考: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)

  

posted @ 2018-11-27 00:14  sjie  阅读(149)  评论(0编辑  收藏  举报