Description
我曾在弦歌之中听过你,
檀板声碎,半出折子戏。
舞榭歌台被风吹去,
岁月深处尚有余音一缕……
Gty神(xian)犇(chong)从来不缺妹子……
他来到了一棵妹子树下,发现每个妹子有一个美丽度……
由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣。
他想知道某个子树中美丽度大于k的妹子个数。
某个妹子的美丽度可能发生变化……
树上可能会出现一只新的妹子……
维护一棵初始有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。
Input
输入第一行包括一个正整数n(1<=n<=30000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。
任何时刻,树上的任何权值大于等于0,且两两不同。
接下来1行,包括n个整数wi,表示初始时每个节点的权值。
接下来1行,包括1个整数m(1<=m<=30000),表示操作总数。
接下来m行,每行包括三个整数 op,u,v:
op,u,v的含义见题目描述。
保证题目涉及的所有数在int内。
Output
对每个op=0,输出一行,包括一个整数,意义见题目描述。
Sample Input
2
1 2
10 20
1
0 1 5
Sample Output
2
Solution
-
大家多用的是树分块的方法,这里我用的归并树+定期重构。
-
具体怎样呢?关键是我们考虑每个修改对之后询问的影响。
-
如果没有修改(静态询问),我们对dfs序建归并树,直接区间查询即可。
-
(归并树就是一种线段树,区间内存的是这个区间权值排序后的序列,查询时在上面二分)
-
有了修改,我们就要判断修改对询问有影响,其中修改点要在询问点的子树内。
-
如何判断是否在子树内:倍增跳。加点时处理出其 2 i 2^i 2i 级父亲。
-
于是我们得到这样一个算法:我们在归并树中查询后,针对若干修改操作暴力判断影响。
-
那我们就可以定期重构啦!
-
如果我们在查询之前的修改不超过 m \sqrt m m 次时,就在归并树上查询后暴力扫描修改计算贡献;
-
如果修改超过了 m \sqrt m m 次时,我们只要根据修改重建一下归并树就可以清除掉这些修改,
-
可以发现归并树的重建不会超过 m \sqrt m m 次。
-
那么我们来分析一下复杂度:(假设 n , m n,m n,m 同阶)
-
每次扫描修改算贡献,修改最多 n \sqrt n n 个,每次倍增判是否在子树要 O ( l o g n ) O(log\ n) O(log n) ,复杂度为 O ( n n l o g n ) O(n\sqrt n\ log\ n) O(nn log n) 。
-
每次重建归并树要 O ( n l o g n ) O(n\ log\ n) O(n log n) ,最多重建 O ( l o g n ) O(log\ n) O(log n) 次,故复杂度同是 O ( n n l o g n ) O(n\sqrt n\ log\ n) O(nn log n) 。
-
于是这题就解决了,总时间复杂度 O ( n n l o g n ) O(n\sqrt n\ log\ n) O(nn log n) 。
-
有一些细节要注意:
-
由于重建归并树常数比较大,我们可以多几次修改再重建一次,比如说 5 ∗ n 5*\sqrt n 5∗n 。
-
还有就是打线段树询问时: f i n d ( 1 , 1 , n ) find(1,1,n) find(1,1,n) ,由于加点时 n n n 会增加,但带进询问时仍然要是之前的 n n n ,不然就不对了,重构时再把 f i n d ( 1 , 1 , n ) find(1,1,n) find(1,1,n) 的 n n n 改成新的。
Code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cctype>
using namespace std;
const int N=60005;
struct data
{
int op,x,y,z;
}q[N>>1];
int n,tot,num,cnt,qx,qy,qz,last,lim;
int first[N],nex[N<<1],en[N<<1];
int w[N],dfn[N],size[N],id[N],dep[N],fa[N][16];
vector<int>ss[N<<2];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void insert(int x,int y)
{
nex[++tot]=first[x];
first[x]=tot;
en[tot]=y;
}
void dfs(int x)
{
dfn[x]=++cnt;
id[cnt]=x;
size[x]=1;
dep[x]=dep[fa[x][0]]+1;
for(int i=first[x];i;i=nex[i])
if(en[i]^fa[x][0])
{
fa[en[i]][0]=x;
dfs(en[i]);
size[x]+=size[en[i]];
}
}
void make(int v,int l,int r)
{
ss[v].clear();
if(l==r)
{
ss[v].push_back(w[id[l]]);
return;
}
int mid=l+r>>1,ls=v<<1,rs=ls|1;
make(ls,l,mid);
make(rs,mid+1,r);
int i=0,ni=ss[ls].size()-1;
int j=0,nj=ss[rs].size()-1;
while(i<=ni && j<=nj) ss[v].push_back(ss[ls][i]<ss[rs][j]?ss[ls][i++]:ss[rs][j++]);
while(i<=ni) ss[v].push_back(ss[ls][i++]);
while(j<=nj) ss[v].push_back(ss[rs][j++]);
}
int find(int v,int l,int r)
{
if(qx<=l && r<=qy) return ss[v].size()-(upper_bound(ss[v].begin(),ss[v].end()--,qz)-ss[v].begin());
int mid=l+r>>1,s=0;
if(qx<=mid) s=find(v<<1,l,mid);
if(qy>mid) s+=find(v<<1|1,mid+1,r);
return s;
}
void dfs1(int x)
{
size[x]=1;
dfn[x]=++cnt;
id[cnt]=x;
for(int i=first[x];i;i=nex[i])
if(en[i]^fa[x][0])
{
dfs(en[i]);
size[x]+=size[en[i]];
}
}
inline void rebuild()
{
cnt=num=0;
dfs1(1);
make(1,1,n);
cnt=n;
}
inline bool belong(int x,int y)
{
if(dep[x]<dep[y]) return false;
for(int i=log2(dep[x]);i>=0;i--)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
return x==y;
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
insert(x,y);
insert(y,x);
}
for(int i=1;i<=n;i++) w[i]=read();
dfs(1);
cnt=n;
for(int j=1;j<16;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
make(1,1,n);
int m=read();
lim=ceil(sqrt(m)*5);
while(m--)
{
int op=read(),x=read()^last,y=read()^last;
if(op==0)
{
if(x<=cnt)
{
qx=dfn[x],qy=dfn[x]+size[x]-1,qz=y;
last=find(1,1,cnt);
}else last=0;
for(int i=1;i<=num;i++)
if(q[i].op==1)
{
if((q[i].y<y)^(q[i].z<y) && belong(q[i].x,x)) last+=q[i].z>y?1:-1;
}else
{
if(q[i].z>y && belong(q[i].x,x)) last++;
}
write(last),putchar('\n');
}else
if(op==1)
{
q[++num]=(data){1,x,w[x],y};
w[x]=y;
if(num==lim) rebuild();
}else
{
q[++num]=(data){2,++n,x,y};
insert(x,n);
insert(n,x);
fa[n][0]=x;
dep[n]=dep[x]+1;
w[n]=y;
for(int i=1;i<16;i++) fa[n][i]=fa[fa[n][i-1]][i-1];
if(num==lim) rebuild();
}
}
return 0;
}