感觉这个题思维难度并不高,问题给的很明确:
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 }