题意
给出一棵n个节点的树,节点1为根节点,每个节点有一个权值,要求资瓷三个操作:
0 x y表示询问以x为根的子树内有多少个节点的权值大于y
1 x y表示将节点x的权值修改为y
2 x y表示插入一个新节点,其父节点为x,权值为y
n,m<=30000
分析
看到这个数据范围如果是序列操作的话就毫不犹豫上分块了,但这是一棵树,所以一开始的想法就是用树剖或者主席树什么的搞一搞,但是插入操作太恶心了,根本资瓷不了。所以就跑去学了一波树分块。
树分块好劲啊!!!
分块的方法是,对于某个节点,若它父亲所在的块还没满则分进父亲所在的块,否则就自成一块,然后向其父亲所在的块连边。
然后就可以转换成对序列操作了。
询问的时候对不完整的块暴力查询,对完整的块二分查找即可。
注意分块数组要开的贼大。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 60005
using namespace std;
int cnt,last[N],block_last[N],pos[N],a[N*400],w[N],tot[N],fa[N],block,tot_block,n,m;
struct edge{int to,next;}e[N*10];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void block_addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=block_last[u];block_last[u]=cnt;
}
void build_block(int x)
{
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa[x]) continue;
fa[e[i].to]=x;
if (tot[pos[x]]<block)
{
pos[e[i].to]=pos[x];
tot[pos[x]]++;
a[(pos[x]-1)*block+tot[pos[x]]]=w[e[i].to];
}else
{
pos[e[i].to]=++tot_block;
tot[tot_block]=1;
a[(pos[e[i].to]-1)*block+1]=w[e[i].to];
block_addedge(pos[x],pos[e[i].to]);
}
build_block(e[i].to);
}
}
void insert(int x)
{
if (tot[pos[fa[x]]]<block)
{
pos[x]=pos[fa[x]];
tot[pos[x]]++;
a[(pos[x]-1)*block+tot[pos[x]]]=w[x];
for (int i=(pos[x]-1)*block+tot[pos[x]];i>(pos[x]-1)*block+1&&a[i]<a[i-1];i--)
swap(a[i],a[i-1]);
}else
{
pos[x]=++tot_block;
tot[tot_block]=1;
block_addedge(pos[fa[x]],pos[x]);
a[(pos[x]-1)*block+1]=w[x];
}
}
void modify(int pos,int x,int y)
{
int u=lower_bound(a+(pos-1)*block+1,a+(pos-1)*block+tot[pos]+1,x)-a;
a[u]=y;
int i=u;
for (int i=u;i<(pos-1)*block+tot[pos]&&a[i]>a[i+1];i++)
swap(a[i],a[i+1]);
for (int i=u;i>(pos-1)*block+1&&a[i]<a[i-1];i--)
swap(a[i],a[i-1]);
}
int block_query(int x,int y)
{
int u=upper_bound(a+(x-1)*block+1,a+(x-1)*block+tot[x]+1,y)-a,ans=0;
if (u<=(x-1)*block+tot[x]) ans+=(x-1)*block+tot[x]-u+1;
for (int i=block_last[x];i;i=e[i].next)
ans+=block_query(e[i].to,y);
return ans;
}
int query(int x,int y)
{
int ans=0;
if (w[x]>y) ans++;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa[x]) continue;
if (pos[e[i].to]==pos[x]) ans+=query(e[i].to,y);
else ans+=block_query(pos[e[i].to],y);
}
return ans;
}
int main()
{
scanf("%d",&n);
block=sqrt(n);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
pos[1]=1;
tot[1]=1;
tot_block=1;
a[1]=w[1];
build_block(1);
for (int i=1;i<=tot_block;i++)
sort(a+(i-1)*block+1,a+(i-1)*block+tot[i]+1);
scanf("%d",&m);
int lastans=0;
for (int i=1;i<=m;i++)
{
int x,y,op;
scanf("%d%d%d",&op,&x,&y);
x^=lastans;y^=lastans;
if (op==0)
{
lastans=query(x,y);
printf("%d\n",lastans);
}else if (op==1)
{
modify(pos[x],w[x],y);
w[x]=y;
}else
{
w[++n]=y;
addedge(x,n);
fa[n]=x;
insert(n);
}
}
return 0;
}