BZOJ 3306 树
3306: 树
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1451 Solved: 475
[Submit][Status][Discuss]
Description
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
Input
第一行两个整数 n, Q ,分别表示树的大小和操作数。
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
Output
对于每个 Q ,输出子树最小值。
Sample Input
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1
Sample Output
1
2
3
4
HINT
对于 100% 的数据:n, Q ≤ 10^5
Source
我们先以1为根dfs并建立倍增数组,然后如果根换成了rt,然后要查询x子树内的最小值。我们分情况讨论:
1)若x==rt,则直接输出整棵树的最小值
2)若lca(x,rt)既不等于x那么直接输出x的子树内的最小值
3)若lca(x,rt)==x那么我们发现整棵树除了x向下走可以到达rt的子树之外全部成了x在rt为根下的子树,那我们把这棵子树中最接近x的节点y求出,在整个区间中踢掉y在1根下子树的范围即可。
#include <bits/stdc++.h> #define ll long long #define eps 1e-7 #define inf 100000000 using namespace std; inline int read(){ int x=0;int f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=1e6+10; struct node{ int y,next; }e[MAXN]; struct sigment_tree{ int mn; }T[MAXN*4]; int linkk[MAXN],len,n,m,in[MAXN],out[MAXN],dfs_clock,dep[MAXN],f[MAXN][25],rt,x,y,val[MAXN],ine[MAXN]; inline void insert(int xx,int yy){ e[++len].y=yy;e[len].next=linkk[xx];linkk[xx]=len; } inline void dfs(int st,int fa){ in[st]=++dfs_clock;dep[st]=dep[fa]+1;f[st][0]=fa;ine[dfs_clock]=st; for(int i=linkk[st];i;i=e[i].next){ dfs(e[i].y,st); } out[st]=dfs_clock; } void getanser(){ for(int i=1;i<=20;i++){ for(int j=1;j<=n;j++){ if(f[j][i-1]) f[j][i]=f[f[j][i-1]][i-1]; } } } inline void add(int l,int r,int root){ if(l>x||r<x) return; if(l==r){ T[root].mn=y;return; } int mid=(r+l)>>1; add(l,mid,root<<1); add(mid+1,r,root<<1|1); T[root].mn=min(T[root<<1].mn,T[root<<1|1].mn); } inline void build(int l,int r,int root){ if(l==r){ T[root].mn=val[ine[l]]; return; } int mid=(l+r)>>1; build(l,mid,root<<1); build(mid+1,r,root<<1|1); T[root].mn=min(T[root<<1].mn,T[root<<1|1].mn); } inline int query(int l,int r,int root){ if(l>y||r<x) return inf; int mid=(l+r)>>1; if(x<=l&&y>=r) return T[root].mn; return min(query(l,mid,root<<1),query(mid+1,r,root<<1|1)); } void init(){ n=read();m=read(); for(int i=1;i<=n;i++){ int xx=read();val[i]=read(); if(xx) insert(xx,i); } dfs(rt=1,0); build(1,n,1); getanser(); } void solve(){ for(int i=1;i<=m;i++){ char ch[5]; scanf("%s",ch);x=read(); if(ch[0]=='V'){ x=in[x];y=read(); add(1,n,1); } else if(ch[0]=='E') rt=x; else{ if(rt==x) printf("%d\n",T[1].mn); else{ if(in[x]<=in[rt]&&out[x]>=out[rt]){ int depth=dep[rt]-dep[x]-1;int yy=rt; for(int i=20;i>=0;i--){ if(depth-(1<<i)>=0) yy=f[yy][i],depth-=(1<<i); } x=1;y=in[yy]-1; int minn=query(1,n,1); x=out[yy]+1;y=n; minn=min(minn,query(1,n,1)); printf("%d\n",minn); } else{ y=out[x];x=in[x]; printf("%d\n",query(1,n,1)); } } } } } int main(){ //freopen("All.in","r",stdin); //freopen("ba.out","w",stdout); init(); solve(); return 0; }
对拍代码
#include <bits/stdc++.h> using namespace std; int main(){ srand(time(int(NULL))); freopen("All.in","w",stdout); int n=rand()%10007;int m=rand()%10007; cout<<n<<' '<<m<<endl; cout<<0<<' '<<rand()%10007<<endl; for(int i=2;i<=n;i++){ int k=rand()%i;if(!k) k++; printf("%d %d\n",k,rand()%10007); } for(int i=1;i<=m;i++){ int k=rand()%3; if(k==0){ printf("V %d %d\n",rand()%n+1,rand()%10007); } if(k==1) printf("Q %d\n",rand()%n+1); if(k==2) printf("E %d\n",rand()%n+1); } return 0; }