正题
这题其实在思路上不难,但是时间却非常卡人。
首先我们看到加点,我们就知道要用分块来解决这个问题,在分块的方式上选择size分块。
然后对于每一个建好的块,建一棵主席树来维护信息。
改权值的时候,我们就需要先把先前的权值的那条链都加上-1,再把新的权值的那条链加上1.
然后再加点时,就根据父亲来看一下,是加入父亲的那个块还是自己新开一个块。
前面dfs建树也是这样。
求有多少个>k的时候就直接块内暴力,儿子块遍历,注意不能直接通过块与块之间的边来遍历,因为不能保证所遍历到的块在子树内。
应该在遍历子树的时候,如果遍历到一个不同块,那么就去这个块的整体,二分答案。
最后ans输出。
这样0操作的时间复杂度是sqrt(n)*log2(n).
而1操作和2操作的时间复杂度都是log2(n)。所以应该是比整块暴力排序要快很多的。
但是我被T飞了。
#include
#include
#include
#include
#include
#include
#define INF 2147483647
using namespace std;
int n;
int sqrtn;
int ls[26000010],rs[26000010];
int tot[26000010];
int f[60010];
int root[60010];
struct edge{
int y,next;
}s[120010];
edge news[120010];
int id[60010];
int first[60010];
int fir[60010];
int d[60010];
int m,tt=0,op,sum[60010],ll=0,len=0;
long long v;
void ins(int x,int y){//原边
len++;
s[len].y=y;s[len].next=first[x];first[x]=len;
}
void inss(int x,int y){
ll++;
news[ll].y=y;news[ll].next=fir[x];fir[x]=ll;
}
inline char nc() {
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
#define getchar() nc()
void read(int &to){
to=0;
char ch=getchar();
int f=1;
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') to=to*10+ch-'0',ch=getchar();
to*=f;
}
void update(int &now,long long l,long long r){//主席树更新动态建点
if(now==0) now=++tt;
tot[now]+=op;
if(l==r) return ;
long long mid=(l+r)/2;
if(v<=mid) update(ls[now],l,mid);
else update(rs[now],mid+1,r);
}
int num=0;
void dfs(int x){//更新x点的所在块
if(sum[id[f[x]]]==sqrtn) {
sum[id[x]=++num]=1;
inss(id[f[x]],id[x]);
}
else sum[id[x]=id[f[x]]]++;
v=d[x];op=1;
update(root[id[x]],0,INF);
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y!=f[x]){
f[y]=x;
dfs(y);
}
}
}
int ans=0;
void findal(int x,int tar){//块搜索,二分答案
for(int i=fir[x];i!=0;i=news[i].next)
findal(news[i].y,tar);
long long l=0,r=INF;
int now=root[x];
if(tar<0){
ans+=tot[now];
return ;
}
while(ly) ans++;
for(int i=first[now];i!=0;i=s[i].next)
if(s[i].y!=f[now]){
if(id[s[i].y]==id[now]) findso(s[i].y,y);
else findal(id[s[i].y],y);
}
}
int get_kth(int x,int y){
ans=0;
findso(x,y);
return ans;
}
void change(int x,int y){//改权值
v=d[x];op=-1;
update(root[id[x]],0,INF);
v=d[x]=y;op=1;
update(root[id[x]],0,INF);
}
void add(int x,int y){//加上一个点
n++;
d[n]=y;
f[n]=x;
ins(x,n);
dfs(n);//直接dfs更新
}
int main(){
read(n);
for(int i=1;i<=n-1;i++){
int x,y;
read(x);read(y);
ins(x,y);ins(y,x);
}
for(int i=1;i<=n;i++)
read(d[i]);
read(m);
sqrtn=sqrt(n)*log2(n);
dfs(1);//从1点开始更新儿子
int type;
int x,y;
int lastans=0;
while(m--){
read(type);read(x);read(y);
x^=lastans;y^=lastans;
if(type==0){
lastans=get_kth(x,y);
printf("%d\n",lastans);
}//分类讨论0
else if(type==1) change(x,y);//1
else if(type==2) add(x,y);//2
}
}