您的位置:首页 > 其它

【bzoj3306】树

2017-07-31 19:44 141 查看
【bzoj3306】树

2014年12月11日2,2940

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。

#include<iostream>

#include<cstring>

#include<cstdio>

#include<algorithm>

#include<cmath>

#define ll long long

#define inf 1000000000 

using namespace std;

inline int read()

{

    int x=0;char ch=getchar();

    while(ch<'0'||ch>'9')ch=getchar();

    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}

    return x;

}

int bin[20];

int n,Q,ind,root,cnt;

int val[100005],l[100005],r[100005],q[100005],deep[100005];

int fa[100005][20];

struct edge{
int to,next;

}e[100005];int last[100005];

struct seg{
int l,r,mn;

}t[400005];

void insert(int u,int v)

{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;

}

void update(int k)

{
t[k].mn=min(t[k<<1].mn,t[k<<1|1].mn);

}

void build(int k,int l,int r)

{
t[k].l=l;t[k].r=r;
if(l==r){t[k].mn=val[q[l]];return;}
int mid=(l+r)>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
update(k);

}

void modify(int k,int pos,int val)

{
int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
if(l==r)
{
t[k].mn=val;return;
}
if(pos<=mid)modify(k<<1,pos,val);
else modify(k<<1|1,pos,val);
update(k);

}

int query(int k,int x,int y)

{
if(x>y)return inf;
int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
if(l==x&&y==r)return t[k].mn;
if(y<=mid)return query(k<<1,x,y);
else if(x>mid)return query(k<<1|1,x,y);
return min(query(k<<1,x,mid),query(k<<1|1,mid+1,y));

}

void dfs(int x)

{
l[x]=++ind;q[ind]=x;
for(int i=1;i<=16;i++)
if(bin[i]<=deep[x])fa[x][i]=fa[fa[x][i-1]][i-1];
else break;
for(int i=last[x];i;i=e[i].next)
{
fa[e[i].to][0]=x;
deep[e[i].to]=deep[x]+1;
dfs(e[i].to);
}
r[x]=ind;

}

int main()

{
bin[0]=1;for(int i=1;i<20;i++)bin[i]=bin[i-1]<<1;//bin数组的租用记录每一位的1,比如1,10,100,100

  //下面有个 bin[i]&d,为了来把点y上提到当前询问点x,挨个上提d的二进制表示的每一位1 
n=read(),Q=read();
for(int i=1;i<=n;i++)
{
int f=read();val[i]=read();
if(f)insert(f,i);
}
dfs(root=1);
build(1,1,n);
char ch[5];int x;
for(int i=1;i<=Q;i++)
{
scanf("%s",ch);x=read();
if(ch[0]=='V')
{
int val=read();
modify(1,l[x],val);
}
else if(ch[0]=='E')root=x;
else
{
if(root==x)printf("%d\n",t[1].mn);
else if(l[x]<=l[root]&&r[x]>=r[root])//如果询问的x的子树最小值,新root是x的子树 
{
int y=root,d=deep[y]-deep[x]-1;//d的作用是新根y和询问x之间的depp差 
for(int i=0;i<=16;i++)
if(bin[i]&d) y=fa[y][i];//找离x最近的y 
printf("%d\n",min(query(1,1,l[y]-1),query(1,r[y]+1,n)));//l[y],r[y]位y节点左右管辖的点的范围

        // query(1,1,l[y]-1),query(1,r[y]+1,n))询问抛去包含新根的子树的询问,因为一换新根,询问x时,新根在x子树下。换新根之后所有x到新根之间的节点都不是x的

        //子节点了,就需要把新根一直抬,抬到和x最近的子节点y,把y包含的区间都砍掉 
}
else 
printf("%d\n",query(1,l[x],r[x]));
}
}
return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: