1. 程式人生 > >bzoj3306:樹(dfn序+線段樹+倍增)

bzoj3306:樹(dfn序+線段樹+倍增)

Problem

支援換根、修改權值的子樹最小值查詢

Solution

不考慮換根就是線段樹模板題了.. 那麼加上換根呢 我們發現換完根,對於原圖中大部分子樹的最小值是沒有關係的 只有 1 到新根上面的點會有變換 新根:為所有點最小值 除新根外此鏈上的點:除去新根這個外枝的其他所有點…(emmm畫個圖看一下) 做法就是 dfn 序建立線段樹…倍增找到要除去的部分剩下的部分即為答案。

Code

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 100010
#define inf 0x3f3f3f3f int n,q,num=0,tot=0,root,fa[N][20],d[N],h[N],in[N],out[N],rl[N],mn[N<<2],a[N]; char op[4]; struct node{int to,next;}mp[N]; inline void insert(int x,int y){ mp[++num].to=y;mp[num].next=h[x];h[x]=num; } inline void dfs(int x){ in[x]=++tot;rl[tot]=x; for(int i=1;i<=16
;i++){ if(!fa[x][i-1]) break; fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(y==fa[x][0]) continue; d[y]=d[x]+1;dfs(y); }out[x]=tot; } inline void update(int v){ mn[v]=min(mn[v<<1],mn[v<<1|1]); } void build(int
v,int l,int r){ if(l==r){ mn[v]=a[rl[l]]; return; }int mid=l+r>>1; build(v<<1,l,mid);build(v<<1|1,mid+1,r); update(v); } void change(int v,int l,int r,int x,int y){ if(l==r){ mn[v]=y; return; }int mid=l+r>>1; if(x<=mid) change(v<<1,l,mid,x,y); else change(v<<1|1,mid+1,r,x,y); update(v); } int query(int v,int l,int r,int x,int y){ if(x>y) return inf; if(x<=l && r<=y) return mn[v]; int mid=l+r>>1,res=inf; if(x<=mid) res=min(res,query(v<<1,l,mid,x,y)); if(mid<y) res=min(res,query(v<<1|1,mid+1,r,x,y)); return res; } int main(){ freopen("a.in","r",stdin); scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%d%d",&fa[i][0],&a[i]); if(i!=1) insert(fa[i][0],i); }d[1]=1;dfs(1);build(1,1,n);root=1; for(int i=1;i<=q;i++){ int x,y;scanf("%s%d",op,&x); if(op[0]=='V'){ scanf("%d",&y); change(1,1,n,in[x],y); }else if(op[0]=='Q'){ if(in[x]<=in[root] && in[root]<=out[x]){ if(x==root) printf("%d\n",mn[1]); else{ y=root; for(int j=16;j>=0;j--) if(d[fa[y][j]]>d[x]) y=fa[y][j]; printf("%d\n",min(query(1,1,n,1,in[y]-1),query(1,1,n,out[y]+1,n))); } }else printf("%d\n",query(1,1,n,in[x],out[x])); }else root=x; } return 0; }