【Luogu】P3979遥远的国度(树链剖分)
不会换根从暑假开始就困扰我了……拖到现在……
会了还是很激动的。
换根操作事实上不需要(也不能)改树剖本来的dfs序……只是在query上动动手脚……
设全树的集合为G,以root为根,u在原根到新根的链上的子树集合为G',则有查询区间=G-G'……
然后查询的时候就查G-G'就行
#include<cstdio> #include<cstdlib> #include<cctype> #include<algorithm> #include<cstring> #define left (rt<<1) #define right (rt<<1|1) #define mid ((l+r)>>1) #define lson l,mid,left #define rson mid+1,r,right #define maxn 100020 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,to; }edge[maxn*3]; int head[maxn],num; inline void add(int from,int to){ edge[++num]=(Edge){head[from],to}; head[from]=num; } int tree[maxn*5]; int tag[maxn*5]; int size[maxn]; int deep[maxn]; int son[maxn]; int father[maxn]; int top[maxn]; int dfn[maxn]; int back[maxn],ID; int q[maxn]; int s[maxn][22]; int n,m; void find(int x,int fa){ deep[x]=deep[fa]+1; size[x]=1; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; father[to]=x; s[to][0]=x; find(to,x); size[x]+=size[to]; if(son[x]==0||size[son[x]]<size[to]) son[x]=to; } } void unionn(int x,int Top){ dfn[x]=++ID; back[ID]=x; top[x]=Top; if(!son[x]) return; unionn(son[x],Top); for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==father[x]||to==son[x]) continue; unionn(to,to); } } inline void pushup(int rt){ tree[rt]=min(tree[left],tree[right]); } void pushdown(int rt){ if(tag[rt]==-1) return; tag[left]=tag[right]=tag[rt]; tree[left]=tree[right]=tag[rt]; tag[rt]=-1; return; } void build(int l,int r,int rt){ tag[rt]=-1; if(l==r){ tree[rt]=q[back[l]]; return; } build(lson); build(rson); pushup(rt); } void memseg(int from,int to,int num,int l,int r,int rt){ if(from<=l&&to>=r){ tree[rt]=tag[rt]=num; //printf("%d %d l=%d r=%d %d\n",from,to,l,r,num); return; } pushdown(rt); if(from<=mid) memseg(from,to,num,lson); if(to>mid) memseg(from,to,num,rson); pushup(rt); return; } int query(int from,int to,int l,int r,int rt){ if(from<=l&&to>=r) return tree[rt]; pushdown(rt); int ans=0x7fffffff; if(from<=mid) ans=min(ans,query(from,to,lson)); if(to>mid) ans=min(ans,query(from,to,rson)); return ans; } int root; void update(int from,int to,int num){ while(top[from]!=top[to]){ if(deep[top[from]]<deep[top[to]]) swap(from,to); memseg(dfn[top[from]],dfn[from],num,1,n,1); from=father[top[from]]; } if(deep[from]>deep[to]) swap(from,to); memseg(dfn[from],dfn[to],num,1,n,1); return; } int LCA(int from,int to){ if(deep[from]<deep[to]) swap(from,to); int f=deep[from]-deep[to]; for(int i=0;(1<<i)<=f;++i) if(f&(1<<i)) from=s[from][i]; if(from==to) return from; for(int i=20;i>=0;--i){ if(s[from][i]==s[to][i]) continue; from=s[from][i]; to=s[to][i]; } return s[from][0]; } inline void prepare(){ for(int j=1;j<=20;++j) for(int i=1;i<=n;++i) s[i][j]=s[s[i][j-1]][j-1]; } int ask(int o){ if(root==1) return query(dfn[o],dfn[o]+size[o]-1,1,n,1); int lca=LCA(root,o); if(lca!=o) return query(dfn[o],dfn[o]+size[o]-1,1,n,1); else{ int now=root; for(int i=20;i>=0;--i) if(deep[s[now][i]]>deep[o]) now=s[now][i]; int ans=0x7fffffff; if(dfn[now]>1) ans=min(ans,query(1,dfn[now]-1,1,n,1)); if(dfn[now]+size[now]<=n) ans=min(ans,query(dfn[now]+size[now],n,1,n,1)); return ans; } } int main(){ n=read(),m=read(); for(int i=1;i<n;++i){ int from=read(),to=read(); add(from,to); add(to,from); } for(int i=1;i<=n;++i) q[i]=read(); root=read(); find(1,1); unionn(1,1); build(1,n,1); prepare(); for(int i=1;i<=m;++i){ int opt=read(); if(opt==1) root=read(); else if(opt==2){ int x=read(),y=read(),z=read(); update(x,y,z); } else{ int x=read(); printf("%d\n",ask(x)); } } return 0; } /* 10 10 1 2 2 3 2 4 1 5 5 6 5 10 5 7 7 8 7 9 5 1 2 3 6 4 7 8 9 10 1 */
话说写博客超简略的我简直是业界毒瘤啊……