BZOJ 3306 树

3306: 树

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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;
}

  

posted @ 2018-01-01 21:19  zhangenming  阅读(258)  评论(0编辑  收藏  举报