bzoj3306:樹(dfn序+線段樹+倍增)
阿新 • • 發佈:2018-12-09
Problem
支援換根、修改權值的子樹最小值查詢
Solution
不考慮換根就是線段樹模板題了.. 那麼加上換根呢 我們發現換完根,對於原圖中大部分子樹的最小值是沒有關係的 只有 到新根上面的點會有變換 新根:為所有點最小值 除新根外此鏈上的點:除去新根這個外枝的其他所有點…(emmm畫個圖看一下) 做法就是 序建立線段樹…倍增找到要除去的部分剩下的部分即為答案。
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;
}