遥远的国度

链接

感觉这个题思维难度并不高,问题给的很明确:

1.换根

2.路径修改

3.子树最小值

2和3都可以用树链剖分解决,所以只用思考问题1.

如果老老实实换根,肯定 TLE 了。

我们发现,不论怎么换根,树的整体形态不变,所以对问题2不会产生影响。

而对问题3,思考可能有几种情况:

//记 pek 为首都,city 为询问的子树

//先以1为根 dfs一遍

1.city在pek的子树中 (即 lca ( pek , city ) ==pek ),那么 city 这棵子树不会发生改变,正常输出。

2.city与pek没什么关系 (即 lca ( pek , city ) ! =pek && lca ( pek , city ) ! = city ) ,那么 city 的子树也不会受到影响,正常输出。

3.pek在city的子树中 (即 lca ( pek , city ) ==city ) , 那么换根以后,pek 所在的这一支儿子子树都成了 city 的祖先,所以扣掉这一棵儿子子树就可以了。

怎么找这棵儿子子树?

可以扫一遍儿子,根据dfs序 子树编号连续 的性质

如果 x 在 y 的子树中,则 pos [ x ] >= in [ y ] && pos [ x ] <= out [ y ] ;

那么挖掉这一块,原来的子树区间变成了两个,取个min,复杂度就加了个常数而已。

就A了

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<string>
  4 #include<cstring>
  5 #define maxn 500005
  6 using namespace std;
  7 
  8 int n,m;
  9 
 10 int fa[maxn],dep[maxn],sz[maxn];
 11 
 12 struct node{
 13     int y,nxt;
 14 }e[maxn*2];
 15 int h[maxn],tot=0;
 16 inline void ad(int x,int y){
 17     ++tot;
 18     e[tot].y=y;e[tot].nxt=h[x];h[x]=tot;
 19 }
 20 //建树 
 21 int id=0;
 22 long long line[maxn];
 23 int pos[maxn],top[maxn],son[maxn];
 24 long long a[maxn];
 25 inline void dfs(int u,int f){
 26     fa[u]=f;
 27     dep[u]=dep[f]+1;
 28     sz[u]=1;
 29     for(int i=h[u],y;i;i=e[i].nxt){
 30         y=e[i].y;
 31         if(y!=f){
 32             dfs(y,u);
 33             sz[u]+=sz[y];
 34             if(sz[y]>sz[son[u]])
 35                 son[u]=y;
 36         }
 37     } 
 38 }
 39 inline void dfs2(int x,int tp){
 40     top[x]=tp;
 41     ++id;
 42     pos[x]=id;
 43     line[id]=a[x];
 44     if(son[x])dfs2(son[x],tp);
 45     for(int i=h[x],y;i;i=e[i].nxt){
 46         y=e[i].y;
 47         if(y!=fa[x]&&y!=son[x]){
 48             dfs2(y,y);
 49         }
 50     }
 51 }
 52 //树剖两遍dfs 
 53 struct tr{
 54     int l,r;
 55     long long mx,cg;
 56 }t[maxn<<2];
 57 inline void pushup(int k){
 58     t[k].mx=min(t[k<<1].mx,t[k<<1|1].mx);
 59 }
 60 inline void biu(int k,int l,int r){
 61     t[k].l=l;
 62     t[k].r=r;
 63     t[k].cg=0;
 64     if(l==r){
 65         t[k].mx=line[l];
 66         return ;
 67     }
 68     int mid=(l+r)>>1;
 69     biu(k<<1,l,mid);
 70     biu(k<<1|1,mid+1,r);
 71     pushup(k);
 72 }
 73 inline void pushd(int k){
 74     if(t[k].cg){
 75         t[k<<1].cg=t[k<<1].mx=t[k].cg;
 76         t[k<<1|1].cg=t[k<<1|1].mx=t[k].cg;
 77         t[k].cg=0;
 78     }
 79 }
 80 inline void change(int k,int x,int y,long long z){
 81     int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
 82     if(l==x&&r==y){
 83         t[k].cg=z;
 84         t[k].mx=z;
 85         return ;
 86     }
 87     pushd(k);
 88     if(y<=mid)change(k<<1,x,y,z);
 89     else if(x>mid)change(k<<1|1,x,y,z);
 90     else{
 91         change(k<<1,x,mid,z);
 92         change(k<<1|1,mid+1,y,z);
 93     }
 94     pushup(k);
 95 }
 96 inline long long ask(int k,int x,int y){
 97     int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
 98     if(l==x&&r==y)return t[k].mx;
 99     pushd(k);
100     if(y<=mid)return ask(k<<1,x,y);
101     else if(x>mid)return ask(k<<1|1,x,y);
102     else return min(ask(k<<1,x,mid),ask(k<<1|1,mid+1,y));
103 }
104 //线段树 
105 inline void change_path(int x,int y,long long z){
106     while(top[x]!=top[y]){
107         if(dep[top[x]]<dep[top[y]])swap(x,y);
108         change(1,pos[top[x]],pos[x],z);
109         x=fa[top[x]];
110     }
111     if(dep[x]<dep[y])swap(x,y);
112     change(1,pos[y],pos[x],z);
113 }
114 inline int lca(int x,int y){
115     while(top[x]!=top[y]){
116         if(dep[top[x]]<dep[top[y]])swap(x,y);
117         x=fa[top[x]];
118     }
119     return dep[x]<=dep[y]?x:y;
120 }
121 //树链剖分的应用 
122 int main()
123 {
124     scanf("%d%d",&n,&m);
125     int x,y;
126     for(int i=1;i<n;i++){
127         scanf("%d%d",&x,&y);
128         ad(x,y);
129         ad(y,x);
130     }
131     for(int i=1;i<=n;i++)
132         scanf("%lld",&a[i]);
133     dfs(1,1);
134     dfs2(1,1);
135     biu(1,1,n);
136     int op;
137     int city,p1,p2;
138     long long v;
139     int pek;
140     int c;
141     scanf("%d",&pek);
142     for(int i=1;i<=m;i++){
143         scanf("%d",&op);
144         if(op==1){
145             scanf("%d",&c);
146             pek=c;continue;
147         }//换根 
148         else if(op==2){
149             scanf("%d%d%lld",&p1,&p2,&v);
150             change_path(p1,p2,v);
151             continue;
152         }//路径修改 
153         else if(op==3){
154             scanf("%d",&city);
155             if(city==pek){
156                 printf("%lld
",t[1].mx);
157                 continue;
158             }//如果是根直接输出 
159             else{
160                 int lc=lca(city,pek);
161                 if(lc==city){
162                     int ll,rr;
163                     for(int j=h[city],y;j;j=e[j].nxt){
164                         y=e[j].y;
165                         if(pos[y]<=pos[pek]&&pos[y]+sz[y]-1>=pos[pek]){
166                             ll=pos[y]-1;
167                             rr=pos[y]+sz[y];
168                             break;
169                         }
170                     }//找pek所在的儿子子树 
171                     long long ans=10000000000;
172                     if(rr<=n)
173                         ans=min(ask(1,1,ll),ask(1,rr,n));
174                     //这里rr可能是n+1,避免re,需要特判一下 
175                     else ans=ask(1,1,ll);
176                     printf("%lld
",ans);
177                     continue;
178                 }//情况3 
179                 else
180                     printf("%lld
",ask(1,pos[city],pos[city]+sz[city]-1));
181                 //其他情况 
182             }
183         }
184     }
185 } 
qwq
原文地址:https://www.cnblogs.com/chiyo/p/10017463.html