[树链剖分]做题记录-遥远的国度
人话题意:请你维护一棵树,支持换根,路径修改,查询子树最小值
其实另外两个操作都是树剖板子,但这个换根有点恶心
所以记录一下
对于换完根查询有几种情况:
1、根就是要查询的点,直接输出子树最小值
2、根在查询节点的子树外,还是直接输出子树最小值
3、根在查询节点的子树里,可以发现是把根节点的祖先,x的直接孩子y砍掉后的整棵树最小值
建议手膜一遍,发现就相当于拎着root把整棵树向上提
然后就乱搞完事
#include#include #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 ){ 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] 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; }