bzoj3306 树
3306: 树
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 597 Solved: 185
[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
树上单点改动、子树查询。非常明显DFS序+线段树。
唯一的问题就是换根操作怎么处理?
事实上我们不须要改变树的形状。仅仅须要记录当前根是哪个节点。每次询问时推断根和询问节点x的位置关系。
① 假设x是根。则输出整棵树的最小值。
② 假设x是根的祖先。则找到根的子树中最接近x的节点y,输出整棵树减去y的子树的最小值。
③ 假设x既不是根也不是根的祖先。则输出x的子树的最小值。
一開始RE了非常多次,由于query中没有推断l>r(事实上如今也不是特别懂)。之后又WA了非常多次。由于找节点y的倍增算法写错了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 100005 #define inf 1000000000 using namespace std; int n,q,rt,x,y,tmp,cnt,tot; int a[maxn],b[maxn],d[maxn],l[maxn],r[maxn],head[maxn],f[maxn][21]; struct edge_type { int next,to; }e[maxn]; struct seg { int l,r,mn; }t[maxn*4]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add_edge(int x,int y) { e[++cnt]=(edge_type){head[x],y};head[x]=cnt; } inline void dfs(int x) { b[++tot]=x;l[x]=tot; F(i,1,20) { if (1<<i<=d[x]) f[x][i]=f[f[x][i-1]][i-1]; else break; } for(int i=head[x];i;i=e[i].next) { d[e[i].to]=d[x]+1; dfs(e[i].to); } r[x]=tot; } inline void pushup(int k) { t[k].mn=min(t[k<<1].mn,t[k<<1|1].mn); } inline void build(int k,int l,int r) { t[k].l=l;t[k].r=r; if (l==r){t[k].mn=a[b[l]];return;} int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); pushup(k); } inline void change(int k,int pos,int x) { if (t[k].l==t[k].r){t[k].mn=x;return;} int mid=(t[k].l+t[k].r)>>1; if (pos<=mid) change(k<<1,pos,x); else change(k<<1|1,pos,x); pushup(k); } inline int query(int k,int l,int r) { if (l>r) return inf; if (t[k].l==l&&t[k].r==r) return t[k].mn; int mid=(t[k].l+t[k].r)>>1; if (r<=mid) return query(k<<1,l,r); else if (l>mid) return query(k<<1|1,l,r); else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r)); } int main() { n=read();q=read(); F(i,1,n) { x=read();a[i]=read(); add_edge(x,i); f[i][0]=x; } rt=1;d[1]=1;dfs(1); build(1,1,n); while(q--) { char ch=getchar(); while (ch<'A'||ch>'Z') ch=getchar(); x=read(); if (ch=='V') { y=read(); change(1,l[x],y); } else if (ch=='E') rt=x; else { if (rt==x) printf("%d\n",t[1].mn); else if (l[x]<=l[rt]&&r[rt]<=r[x]) { int dis=d[rt]-d[x]-1;y=rt; F(i,0,20) if ((1<<i)&dis) y=f[y][i]; printf("%d\n",min(query(1,1,l[y]-1),query(1,r[y]+1,n))); } else printf("%d\n",query(1,l[x],r[x])); } } return 0; }