题目链接
题意
维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。
支持以下操作:
0 u x 询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)
1 u x 把u节点的权值改成x。(u^=lastans,x^=lastans)
2 u x 添加一个编号为”当前树中节点数+1”的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)
最开始时lastans=0。(强制在线)
(1<=n<=30000,1<=m<=30000)
题解
与子树相关的问题我们能想到树剖,dfs序等东西。但是这道题要支持加点与大于x数的个数的统计。这样就不是很好做了。
但是注意到修改是单点的,并且n不是非常大。我们可以分块乱搞。
我们先确定一个块的大小S,将树分成若干个联通块,每块的大小都不超过S。
我们在每个块内将权值从小到大排序。
询问时,若在一个整块内,我们二分查找,否则暴力。
修改是暴力维护块内的权值有序。
加点时判断父亲所在块大小是否等于S,等于的话该点单独成一块,否则加入父亲所在块内,注意也要维护块内权值有序。
由于有二分操作,块的大小取
n−−√∗log2n
最合适。(我也不会证明,但亲测是这样)。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
#define N 60010
#define M 60010
struct node{
int pos,top,data,fa;
}t[N];
struct block{
int size;
vector<int>v;
}b[M];
struct edge{
int x,next;
}e[N*2][2];
int x,y,n,m,tot[2],num,first[N][2],ans,lim,op;
queue<int>q;
void add(int x,int y,int k){
e[++tot[k]][k].x=y;
e[tot[k]][k].next=first[x][k];
first[x][k]=tot[k];
}
void newnode(int x,int top,int pos){
b[pos].v.push_back(t[x].data);
b[pos].size++;
t[x].pos=pos;
t[x].top=top;
}
void bfs(){
newnode(1,1,++num); q.push(1);
while(!q.empty()){
x=q.front(); q.pop();
for(int i=first[x][0];i;i=e[i][0].next)
if(e[i][0].x!=t[x].fa){
if(b[t[x].pos].size>=lim){
newnode(e[i][0].x,e[i][0].x,++num);
add(t[x].pos,num,1);
}else newnode(e[i][0].x,t[x].top,t[x].pos);
t[e[i][0].x].fa=x;
q.push(e[i][0].x);
}
}
}
int count(int x,int y){
int l=0,r=b[x].v.size(),mid;
while((r-l)>1){
mid=(l+r)>>1;
if(b[x].v[mid]>y)r=mid;
else l=mid;
}
if(b[x].v[l]>y)r=l;
int tmp=b[x].v.size()-r;
for(int i=first[x][1];i;i=e[i][1].next)
tmp+=count(e[i][1].x,y);
return tmp;
}
int ask(int x,int y){
if(t[x].top==x)return count(t[x].pos,y);
int tmp=(t[x].data>y);
for(int i=first[x][0];i;i=e[i][0].next)
if(e[i][0].x!=t[x].fa) tmp+=ask(e[i][0].x,y);
return tmp;
}
void modify(int x,int y){
int p=t[x].pos,n=b[p].v.size(),i;
for(i=0;i<n;i++)
if(b[p].v[i]==t[x].data)break;
t[x].data=b[p].v[i]=y;
for(;i<n-1&&b[p].v[i]>b[p].v[i+1];i++)
swap(b[p].v[i],b[p].v[i+1]);
for(;i>0&&b[p].v[i]<b[p].v[i-1];i--)
swap(b[p].v[i],b[p].v[i-1]);
}
void insert(int x,int y){
add(x,y,0);
if(b[t[x].pos].size>=lim){
newnode(y,y,++num);
add(t[x].pos,num,1);
}else newnode(y,t[x].top,t[x].pos);
int i=b[t[y].pos].v.size()-1;
for(;i>0&&b[t[y].pos].v[i]<b[t[y].pos].v[i-1];i--)
swap(b[t[y].pos].v[i],b[t[y].pos].v[i-1]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y,0);
add(y,x,0);
}
for(int i=1;i<=n;i++) scanf("%d",&t[i].data);
lim=(int)sqrt(n)*(log(n)/log(2));
bfs();
for(int i=1;i<=num;i++)sort(b[i].v.begin(),b[i].v.end());
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&op,&x,&y);
x^=ans; y^=ans;
if(op==0) ans=ask(x,y),printf("%d\n",ans);
else if(op==1)modify(x,y);
else if(op==2) {
t[++n].data=y;
insert(x,n);
}
}
return 0;
}