1. 程式人生 > 其它 >[樹鏈剖分]做題記錄-遙遠的國度

[樹鏈剖分]做題記錄-遙遠的國度

人話題意:請你維護一棵樹,支援換根,路徑修改,查詢子樹最小值

其實另外兩個操作都是樹剖板子,但這個換根有點噁心

所以記錄一下

對於換完根查詢有幾種情況:

1、根就是要查詢的點,直接輸出子樹最小值

2、根在查詢節點的子樹外,還是直接輸出子樹最小值

3、根在查詢節點的子樹裡,可以發現是把根節點的祖先,x的直接孩子y砍掉後的整棵樹最小值

      建議手膜一遍,發現就相當於拎著root把整棵樹向上提

然後就亂搞完事

#include<cstdio>
#include<cstring>
#include<string>
#define WR WinterRain
#define
int long long using namespace std; const int WR=1001000,INF=2147483647; struct Edge{ int pre,to; }edge[WR<<2]; struct SegmentTree{ int l,r,val,lzy; void SegementTree(){val=INF;} }tree[WR<<2]; int n,m; int a[WR]; int root; int head[WR],tot; int fa[WR][21],son[WR],sze[WR],dpt[WR];
int top[WR],id[WR],rnk[WR],cnt; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-'0'; ch=getchar(); } return s*w; }
void pushup(int k){ tree[k].val=min(tree[k<<1].val,tree[k<<1|1].val); } void pushdown(int k){ tree[k<<1].val=tree[k].lzy; tree[k<<1|1].val=tree[k].lzy; tree[k<<1].lzy=tree[k].lzy; tree[k<<1|1].lzy=tree[k].lzy; tree[k].lzy=0; } void build(int k,int l,int r){ tree[k].l=l,tree[k].r=r; if(l==r){ tree[k].val=a[rnk[l]]; return; } int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); pushup(k); } void modify(int k,int st,int ed,int v){ if(tree[k].l>=st&&tree[k].r<=ed){ tree[k].lzy=v; tree[k].val=v; return; } if(tree[k].lzy) pushdown(k); int mid=(tree[k].l+tree[k].r)>>1; if(st<=mid) modify(k<<1,st,ed,v); if(ed>mid) modify(k<<1|1,st,ed,v); pushup(k); } int query(int k,int l,int r){ if(l>r) return INF; if(tree[k].l>=l&&tree[k].r<=r){ return tree[k].val; } if(tree[k].lzy) pushdown(k); int mid=(tree[k].l+tree[k].r)>>1,res=INF; if(l<=mid) res=min(res,query(k<<1,l,r)); if(r>mid) res=min(res,query(k<<1|1,l,r)); return res; } void add(int u,int v){ edge[++tot].pre=head[u]; edge[tot].to=v; head[u]=tot; } void dfs1(int u,int rt){ fa[u][0]=rt,dpt[u]=dpt[rt]+1,sze[u]=1; for(int i=1;i<=20;i++){ fa[u][i]=fa[fa[u][i-1]][i-1]; if(!fa[u][i]) break; } for(int i=head[u];i;i=edge[i].pre){ int v=edge[i].to; if(v==rt) continue; dfs1(v,u); sze[u]+=sze[v]; if(sze[v]>sze[son[u]]) son[u]=v; } } void dfs2(int u,int tp){ top[u]=tp,id[u]=++cnt,rnk[cnt]=u; if(son[u]) dfs2(son[u],tp); for(int i=head[u];i;i=edge[i].pre){ int v=edge[i].to; if(v!=fa[u][0]&&v!=son[u]) dfs2(v,v); } } void path_modify(int x,int y,int v){ while(top[x]!=top[y]){ if(dpt[top[x]]<dpt[top[y]]) swap(x,y); modify(1,id[top[x]],id[x],v); x=fa[top[x]][0]; } if(dpt[x]>dpt[y]) swap(x,y); modify(1,id[x],id[y],v); } int getfa(int x,int k){ for(int i=20;i>=0;i--){ if(k>=(1<<i)) x=fa[x][i],k-=(1<<i); } return x; } signed main(){ n=read(),m=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v);add(v,u); } for(int i=1;i<=n;i++) a[i]=read(); //root要等於1 dfs1(1,0); dfs2(1,1); root=read(); build(1,1,n); for(int i=1;i<=m;i++){ int opt=read(); if(opt==1) root=read(); if(opt==2){ int x=read(),y=read(),v=read(); path_modify(x,y,v); } if(opt==3){ int x=read(); if(x==root) printf("%lld\n",tree[1].val); else{ int tmp=getfa(root,dpt[root]-dpt[x]-1); if(id[x]<id[root]&&fa[tmp][0]==x){ //畫圖試試會發現如果root在x的子樹裡答案是 //把root的遠祖,x的直接孩子y的子樹砍掉的部分 //可以想象拎著根節點向上提 printf("%lld\n",min(query(1,1,id[tmp]-1),query(1,id[tmp]+sze[tmp],n))); }else printf("%lld\n",query(1,id[x],id[x]+sze[x]-1)); } } } return 0; }