现在的位置: 首页 > 综合 > 正文

树 (罗雨屏)

2018年01月14日 ⁄ 综合 ⁄ 共 3265字 ⁄ 字号 评论关闭
试题来源
  IOI2012中国国家队训练
问题描述
  给定一棵大小为 n 的有根点权树,支持以下操作:

  • 换根

  • 修改点权



  • 查询子树最小值
输入格式
  第一行两个整数 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 的子树最小值
输出格式
  对于每个 Q ,输出子树最小值。
样例输入
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
样例输出
1

2

3

4
数据规模和约定
  对于 100% 的数据:n, Q ≤ 10^5。

题目大意就是给你一棵树,支持换根、修改点权、求一个点的子树的最小值。

对于这道题,如果没有换根操作,就直接上RMQ。

然而有了换根,我们可以先以1号点为根处理出这棵树的dfs序用线段树来维护一段dfs序的最小值,修改点权就不说了。

对于每个查询

设查询的点X;

设此时根为R;

设L[X]代表X在dfs序中第一次出现的位置

设R[X]代表X在dfs序中第一次出现的位置

如图的一棵树

1:当X等于R时 比如X=R=1

直接输出整棵树的最小值。

2:当R不在X的子树中 比如X=2 R=3

明显就是X的子树中的最小值

即为L[X]到R[X]的最小值

3:当X是R的祖先时

首先确定包含点的范围:

举两个例子:当X=2、R=4时是1、2、3、5
当X=1、R=4时是1、3。

通过这两个例子,我们可以看出包含的范围就是整棵树除去E所在的子树(E是X的儿子,也是R的祖先)。

那么答案就是L[1]到L[E]-1与R[E]+1到R[1]的最小值

(这一点要仔细想想,当初也是花了好久才弄懂)

最后说一下几个要注意的地方吧

求dfs序,不能直接dfs,会爆栈,所以。。。。手动栈吧

找E的时候不能一步一步往上跳,用倍增吧

Ps:其实上面两点都不要管就可以过原始数据,只是我们oj丧心病狂处处卡人啊。。。。

附代码

#include<cstdio>
#include<iostream>
#include<cmath>
const double eps=0.0000001;
const int maxn=100010*2,maxm=maxn*8;
using namespace std;
int tot=0;
int pre[maxn],son[maxn];
int now[maxn];
void add(int a,int b){
	pre[++tot]=now[a];
	now[a]=tot;
	son[tot]=b;
}

inline int read(){
    char ch=getchar(); int tmp=0;
    while (ch<'0' || ch>'9') ch=getchar();
    for (;ch<='9' && ch>='0';ch=getchar()) tmp=tmp*10+ch-'0';
    return tmp;
}
int n,m;
int a[maxn];
void init(){
	n=read();
	m=read();
	for (int i=1;i<=n;++i){
		int a=read();
		int b=read();
		add(a,i);
		::a[i]=b;
	}
}
int l[maxn],r[maxn];
int dep[maxn];
int sum=0;
int maxdep=0;
int fa[maxn][40];
int num[maxn];
int syssta[maxn];
int bre[maxn];
void dfs(){
	int tot=0;
	syssta[++tot]=1;
	dep[1]=1;
	for (int i=1;i<=n;++i)
		bre[i]=now[i];
	while (tot){
		if (bre[syssta[tot]]==now[syssta[tot]]){
			l[syssta[tot]]=++sum;
			num[sum]=a[syssta[tot]];
		}
		if (!bre[syssta[tot]]){
			r[syssta[tot]]=++sum;
			num[sum]=a[syssta[tot]];
			tot--;
		}
		else {
			syssta[tot+1]=son[bre[syssta[tot]]];
			fa[son[bre[syssta[tot]]]][0]=syssta[tot];
			bre[syssta[tot]]=pre[bre[syssta[tot]]];
			dep[syssta[tot+1]]=dep[syssta[tot]]+1;
			maxdep=max(maxdep,dep[syssta[tot+1]]);
			tot++;
		}
	}
}

void prepare(){
	int quit=(int)(log(maxdep)/log(2)+eps);
	for (int i=1;i<=quit;++i)
		for (int j=1;j<=n;++j)
			fa[j][i]=fa[fa[j][i-1]][i-1];
}

inline int find(int x,int root){
	swap(x,root);
	int gap=dep[x]-dep[root]-1;
	while (gap){
		int tmp=(int)(log(gap)/log(2)+eps);
		x=fa[x][tmp];
		gap-=1<<tmp;
	}
	return x;
}
int tree[maxm];
void inline updata(int p){
	int Ls=p*2,Rs=Ls+1;
	tree[p]=tree[Ls]<tree[Rs]?tree[Ls]:tree[Rs];
}
int wh[maxn];
void build(int p,int l,int r){
	if (l==r){
		tree[p]=num[l];
		wh[l]=p;
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	updata(p);
}

void ins(int p,int add){
	tree[p]=add;
	p=p>>1;
	while (p){
		updata(p);
		p=p>>1;
	}
}
inline int min(int a,int b){
	return a<b?a:b;
}
int countit(int p,int l,int r,int fir,int las){
	if (l==fir && r==las)
		return tree[p];
	int mid=(l+r)/2;
	int Ls=p*2,Rs=Ls+1;
	if (las<=mid) return countit(Ls,l,mid,fir,las);
	if (fir>mid) return countit(Rs,mid+1,r,fir,las);
	int ans=countit(Ls,l,mid,fir,mid);
	ans=min(ans,countit(Rs,mid+1,r,mid+1,las));
	return ans;
}

void work(){
	dep[1]=1;
	dfs();
	prepare();
	n*=2;
	build(1,1,n);
	int root=1;
	while (m--){
		char tmp[3];
		scanf("%s",tmp);
		if (tmp[0]=='V'){
			int a=read();
			int b=read();
			ins(wh[l[a]],b);
			ins(wh[r[a]],b);
		}
		if (tmp[0]=='E')
			root=read();
		if (tmp[0]=='Q'){
			int x=read();
			int ans;
			if (x==root) ans=tree[1];
			else
			if (l[x]>l[root] || r[x]<r[root]) 
				ans=countit(1,1,n,l[x],r[x]);
			else{
				int xx=find(x,root);
				ans=min(countit(1,1,n,1,l[xx]-1),countit(1,1,n,r[xx]+1,n));
			}
			printf("%d\n",ans);
		}
	}
}

int main(){
	init();
	work();
	return 0;
}

【上篇】
【下篇】

抱歉!评论已关闭.