题目背景
我曾在弦歌之中听过你,
檀板声碎,半出折子戏。
舞榭歌台被风吹去,
岁月深处尚有余音一缕……
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。
输入输出格式
输入格式:
输入第一行包括一个正整数n(1<=n<=30000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。
任何时刻,树上的任何权值大于等于0,且两两不同。
接下来1行,包括n个整数wi,表示初始时每个节点的权值。
接下来1行,包括1个整数m(1<=m<=30000),表示操作总数。
接下来m行,每行包括三个整数 op,u,x:
op,u,x的含义见题目描述。
保证题目涉及的所有数在int内。
输出格式:
对每个op=0,输出一行,包括一个整数,意义见题目描述。
输入输出样例
输入样例#1:
2
1 2
10 20
1
0 1 5
输出样例#1:
2
输入样例#2:
2
1 2
10 20
1
0 1 10
输出样例#2:
1
说明
时间限制每个测试点1s
空间限制128M
分析:
我们可以对时间分块,每个节点开一个
记录子树中的所有数,可以归并维护。对于修改操作(包括连边),开一个栈,先在原来的树中查询,然后再看这些修改连边操作能不能影响答案,也就是判断修改点是否在查询点的子树中,可以开一个st表搞一下。如果栈中的操作大于一个值
,那么直接暴力重构这棵树。重构是
的,查询是
的,所以要把块建得尽量大一点。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int maxn=6e4+7;
const int maxe=1e5+7;
int n,cnt,test,lastans,block,op,x,y;
int ls[maxn],f[maxn][20],w[maxn],a[maxn],dep[maxn];
vector <int> t[maxn];
struct edge{
int y,next;
}g[maxe];
struct rec{
int x,w,pre;
};
vector <rec> q;
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void merge(int x,int y)
{
int l=0,r=0,num=0;
while ((l<t[x].size()) || (r<t[y].size()))
{
if (l>=t[x].size()) a[++num]=t[y][r++];
else
{
if (r>=t[y].size()) a[++num]=t[x][l++];
else
{
if (t[x][l]<t[y][r]) a[++num]=t[x][l++];
else a[++num]=t[y][r++];
}
}
}
t[x].clear();
for (int i=1;i<=num;i++) t[x].push_back(a[i]);
}
void rebuild(int x,int fa)
{
if (!f[x][0]) f[x][0]=fa;
if (!dep[x]) dep[x]=dep[fa]+1;
t[x].clear();
t[x].push_back(w[x]);
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
rebuild(y,x);
merge(x,y);
}
}
int query(int x,int k)
{
if (t[x].empty()) return 0;
int l=0,r=t[x].size()-1;
int sum=0;
while (l<r)
{
int mid=(l+r)/2;
if (t[x][mid+1]>k) sum+=(r-mid),r=mid;
else l=mid+1;
}
if (t[x][l]>k) sum++;
return sum;
}
bool isfather(int x,int y)
{
if (dep[x]>dep[y]) return 0;
for (int d=dep[y]-dep[x];d;d-=d&(-d))
{
int k=trunc(log((d&(-d))+0.5)/log(2));
y=f[y][k];
}
return (x==y);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
rebuild(1,0);
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
}
scanf("%d",&test);
lastans=0;
block=trunc(sqrt(test*10));
for (int i=1;i<=test;i++)
{
scanf("%d%d%d",&op,&x,&y);
x^=lastans; y^=lastans;
if (op==0)
{
if (q.size()>block)
{
rebuild(1,0);
q.clear();
}
lastans=query(x,y);
for (int i=0;i<q.size();i++)
{
if (isfather(x,q[i].x))
{
if ((q[i].pre<=y) && (q[i].w>y)) lastans++;
if ((q[i].pre>y) && (q[i].w<=y)) lastans--;
}
}
printf("%d\n",lastans);
}
if (op==1)
{
q.push_back((rec){x,y,w[x]});
w[x]=y;
}
if (op==2)
{
n++;
add(x,n);
f[n][0]=x;
dep[n]=dep[x]+1;
q.push_back((rec){n,y,w[n]});
w[n]=y;
for (int j=1;j<20;j++) f[n][j]=f[f[n][j-1]][j-1];
}
}
}