您的位置:首页 > 理论基础 > 数据结构算法

树 (罗雨屏)

2013-10-29 11:12 477 查看
试题来源

  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(
a665
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息