【bzoj3306】树

来源:互联网 发布:av淘宝地址更新 编辑:程序博客网 时间:2024/06/07 22:20

【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;
}
原创粉丝点击