bzoj3083: 遥远的国度
树链剖分。
除去换根的俩个操作,在树链剖分后很容易实现。
换根以后只需操作的时候进行分类讨论即可。
操作2不会受到换根的影响。
操作3:如果询问点就是根,输出整个线段树最小值。
如果询问点在根到1的路径上,查询(1,st[p])和(ed[p]+1,n)的最小值,st和ed分别为出入栈的编号,p为路径上最靠近询问点的点。
其他情况下,输出范围(st[u],ed[u])的最小值,u为询问点。
细节方面要注意的是,一定要求p,因为ed[p+1]不一定等于ed[u],u为询问点。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 200000 + 10; const int maxm= 1600000 + 10; const int inf = 0x7fffffff; const int maxh = 25; int g[maxn],v[maxm],next[maxm],eid; int root[maxm],st[maxm],ed[maxm]; int dep[maxn],anc[maxn][maxh],size[maxn],son[maxn]; int n,m,vid,cap,op,x,y,val; void addedge(int a,int b) { v[eid]=b; next[eid]=g[a]; g[a]=eid++; v[eid]=a; next[eid]=g[b]; g[b]=eid++; } struct Segtree { #define lc(x) ((x)<<1) #define rc(x) (((x)<<1)|1) int l[maxm],r[maxm]; int minv[maxm],sam[maxm]; inline void update(int x) { minv[x]=min(minv[lc(x)],minv[rc(x)]); } void push(int x) { if(!sam[x]) return; minv[lc(x)]=sam[lc(x)]=sam[x]; minv[rc(x)]=sam[rc(x)]=sam[x]; sam[x]=0; } void change(int x,int L,int R,int val) { if(L>r[x] || R<l[x]) return; if(L<=l[x] && r[x]<=R) { minv[x]=sam[x]=val; return; } push(x); change(lc(x),L,R,val); change(rc(x),L,R,val); update(x); } int query(int x,int L,int R) { if(L>r[x] || R<l[x]) return inf; if(L<=l[x] && r[x]<=R) return minv[x]; push(x); return min(query(lc(x),L,R),query(rc(x),L,R)); } void build(int x,int L,int R) { l[x]=L; r[x]=R; if(L==R) { minv[x]=sam[x]=0; return; } int mid=(L+R)>>1; build(lc(x),L,mid); build(rc(x),mid+1,R); } }seg; void dfs1(int u) { dep[u]=dep[anc[u][0]]+1; size[u]=1; for(int h=1;h<maxh;h++) anc[u][h]=anc[anc[u][h-1]][h-1]; for(int i=g[u];~i;i=next[i]) if(v[i]!=anc[u][0]) { anc[v[i]][0]=u; dfs1(v[i]); size[u]+=size[v[i]]; if(size[v[i]]>size[son[u]]) son[u]=v[i]; } } void dfs2(int u,int r) { root[u]=r; st[u]=++vid; //ID[vid]=u; if(son[u]) dfs2(son[u],r); for(int i=g[u];~i;i=next[i]) if(v[i]!=anc[u][0] && v[i] != son[u]) dfs2(v[i],v[i]); ed[u]=vid; } int LCA(int a,int b) { if(dep[a]<dep[b]) swap(a,b); for(int h=maxh-1;h>=0;h--) if(dep[anc[a][h]]>=dep[b]) a=anc[a][h]; if(a==b) return a; for(int h=maxh-1;h>=0;h--) if(anc[a][h]!=anc[b][h]) { a=anc[a][h]; b=anc[b][h]; } return anc[a][0]; } void alter(int x,int y,int v) { while(root[y]!=root[x]) { seg.change(1,st[root[y]],st[y],v); y=anc[root[y]][0]; } seg.change(1,st[x],st[y],v); } int ANC(int x,int k) { for(int h=0;h<maxh && k;h++,k>>=1) if(k&1) x=anc[x][h]; return x; } void query(int id) { if(cap==id) printf("%d\n",seg.query(1,1,n)); else if(LCA(cap,id)==id) { int p=ANC(cap,dep[cap]-dep[id]-1); printf("%d\n",min(seg.query(1,1,st[p]-1),seg.query(1,ed[p]+1,n))); } else printf("%d\n",seg.query(1,st[id],ed[id])); } int main() { memset(g,-1,sizeof(g)); scanf("%d%d",&n,&m); for(int i=1,a,b;i<n;i++) { scanf("%d%d",&a,&b); addedge(a,b); } anc[1][0]=1; cap=1; seg.build(1,1,n); dfs1(1); dfs2(1,1); for(int i=1,a;i<=n;i++) { scanf("%d",&a); seg.change(1,st[i],st[i],a); } scanf("%d",&cap); while(m--) { scanf("%d%d",&op,&x); if(op==1) cap=x; else if(op==2) { scanf("%d%d",&y,&val); int lca=LCA(x,y); alter(lca,x,val); alter(lca,y,val); } else query(x); } return 0; }