Description
我曾在青山之中遇过你,
新竹做杖,鬓插紫茱萸。
跣足踏过无边丝雨,
又拾起燕川雪片片落如席……
Gty神(xian)犇(chong)从来不缺妹子……
他又来到了一棵妹子树下,发现每个妹子有一个美丽度……
由于Gty很 哲♂学 也很 机♂智,他只对美丽度大于某个值的妹子感兴趣。
他想知道某个子树中美丽度大于x的妹子个数。
某个妹子的美丽度可能发生变化……
树上可能会出现一只新的妹子……
但是……树枝可能会断裂,于是,Gty惊讶地发现,他的面前变成了一片妹子树组成的森林……
维护一棵初始有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)
3 u 删除节点u与其父节点之间的路径。此时u的父节点变成叶子节点,u变成分裂出的树的根。(u^=lastans)
最开始时lastans=0。
Input
输入第一行包括一个正整数n(1<=n<=100000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。
任何时刻,树上的任何权值大于等于0,且两两不同。
接下来1行,包括n个整数wi,表示初始时每个节点的权值。
接下来1行,包括1个整数m(1<=m<=100000),表示操作总数。
接下来m行,每行最开始包括一个整数op,
若op=3,该行还会有一个整数u;
若op不等于3,该行还会有两个整数u,x;
op,u,x的范围见题目描述。
保证题目涉及的所有数在int内。
Output
对每个op=0,输出一行,包括一个整数,意义见题目描述。
Sample Input
2
1 2
10 20
1
0 1 5
Sample Output
2
Solution
-
bzoj 3720 相比,本题多了一个断边操作。
-
这题的解法是树分块,顾名思义就是将树分成一块一块的。
-
具体来说就是设置一个阈值 l i m lim lim ,并将树根设为第 1 1 1 块。
-
开始递归到下一个点 x x x ,若上一个点所在块的 s i z e < l i m size<lim size<lim ,则将 x x x 也归入上一个块;
-
若上一个点所在块的 s i z e size size 已经 = l i m =lim =lim 了,那么就将 x x x 作为新的一个块的根。
-
这样递归下去,树就被分成了 n l i m \frac{n}{lim} limn 个块了。
-
对于每个块中的点,我们开一个 v e c t o r vector vector 记录其排好序的权值。
-
对于查询操作,散块暴力,整块查询 ,从查询点先遍历几个散点,之后就能直接跑整块了,这样跑的整块和散点个数都是有限的,因为有着 l i m lim lim 的限制。每个整块中二分出符合条件的位置即可。
-
对于修改操作,我们直接在修改点所在块的 v e c t o r vector vector 中把该点的权值找出来,替换成新权值,注意此时应维护 v e c t o r vector vector 中元素的有序性。
-
对于加点操作,我们和当初树分块一样即可(看看能不能归入父亲节点所在块),注意当能加入旧块时,仍要保持 v e c t o r vector vector 元素的有序性。
-
对于删除操作 ,若删除点为一个块的根节点,则直接断开其跟上一个块的连边;否则就将该点下面的建成一个新的块,多注意细节即可。
-
这样处理就能较好地保证各操作的复杂度,注意常数即可通过本题。
-
若阈值 l i m lim lim 设为 n \sqrt n n ,则本题复杂度约为 O ( n n l o g n ) O(n\sqrt n\ log\ n) O(nn log n) 。
-
由于块的个数多反而常数大,我们可以使 l i m lim lim 设得稍大一点,如 l i m = n l o g n lim=\sqrt{n\ log\ n} lim=n log n 。
-
这样就能跑得挺快的了。
-
唯一美中不足的是树分块做法似乎能被菊花图卡(块的个数不可抑制的变得特别多)。
-
但是本题好像没有别的做法了,这也许就是我们应该努力的地方啊!
Code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cctype>
using namespace std;
const int N=2e5+5;
int cnt,lim,last,val;
int w[N],id[N],size[N],fa[N],head[N];
vector<int>ss[N],e[N],e1[N];
vector<int>::iterator it;
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)
{
e[x].push_back(y);
e[y].push_back(x);
}
inline void insert1(int x,int y)
{
e1[x].push_back(y);
}
void dfs(int x)
{
id[x]=cnt;
size[cnt]++;
ss[cnt].push_back(w[x]);
for(int i=0;i<(int)e[x].size();i++)
{
int to=e[x][i];
if(to^fa[x])
{
fa[to]=x;
if(size[id[x]]==lim)
{
head[++cnt]=to;
insert1(id[x],cnt);
}
dfs(to);
}
}
}
void dfs2(int x)
{
last+=ss[x].size()-(upper_bound(ss[x].begin(),ss[x].end()--,val)-ss[x].begin());
for(int i=0;i<(int)e1[x].size();i++) dfs2(e1[x][i]);
}
void dfs1(int x)
{
last+=w[x]>val;
for(int i=0;i<(int)e[x].size();i++)
{
int to=e[x][i];
if(to^fa[x])
if(id[to]==id[x]) dfs1(to); else dfs2(id[to]);
}
}
inline void del(int x,int y)
{
e[x].erase(find(e[x].begin(),e[x].end(),y));
e[y].erase(find(e[y].begin(),e[y].end(),x));
}
inline void del1(int x,int y)
{
it=find(e1[x].begin(),e1[x].end(),y);
if(it!=e1[x].end()) e1[x].erase(it);
}
void dfs3(int x)
{
id[x]=cnt;
size[cnt]++;
ss[cnt].push_back(w[x]);
for(int i=0;i<(int)e[x].size();i++)
{
int to=e[x][i];
if(to^fa[x])
if(id[to]==val) dfs3(to); else
{
del1(val,id[to]);
insert1(cnt,id[to]);
}
}
}
int main()
{
freopen("bzoj3731.in","r",stdin);
freopen("bzoj3731.out","w",stdout);
int n=read();
for(int i=1;i<n;i++) insert(read(),read());
for(int i=1;i<=n;i++) w[i]=read();
lim=ceil(sqrt(n*log2(n)));
head[cnt=1]=1;
dfs(1);
for(int i=1;i<=cnt;i++) sort(ss[i].begin(),ss[i].end());
int m=read();
while(m--)
{
int op=read(),x=read()^last;
//int op=read(),x=read();
if(op==3)
{
if(head[id[x]]==x)
{
if(fa[x])
{
del(x,fa[x]);
del1(id[fa[x]],id[x]);
}
}else
{
del(x,fa[x]);
val=id[x];
head[++cnt]=x;
dfs3(x);
sort(ss[cnt].begin(),ss[cnt].end());
int idfa=id[fa[x]];
size[idfa]-=size[cnt];
for(int i=0;i<(int)ss[cnt].size();i++)
{
int pos=lower_bound(ss[idfa].begin(),ss[idfa].end()--,ss[cnt][i])-ss[idfa].begin();
ss[idfa].erase(ss[idfa].begin()+pos);
}
}
}else
{
int y=read()^last;
//int y=read();
if(!op)
{
last=0;
val=y;
if(head[id[x]]==x) dfs2(id[x]); else dfs1(x);
write(last),putchar('\n');
}else
if(op==1)
{
int idx=id[x],pos=lower_bound(ss[idx].begin(),ss[idx].end()--,w[x])-ss[idx].begin();
if(y<ss[idx][pos])
{
while(pos && ss[idx][pos-1]>y)
{
ss[idx][pos]=ss[idx][pos-1];
pos--;
}
ss[idx][pos]=y;
}else
{
int up=ss[idx].size()-1;
while(pos<up && ss[idx][pos+1]<y)
{
ss[idx][pos]=ss[idx][pos+1];
pos++;
}
ss[idx][pos]=y;
}
w[x]=y;
}else
{
fa[++n]=x;
w[n]=y;
insert(x,n);
if(size[id[x]]==lim)
{
head[++cnt]=n;
size[cnt]=1;
id[n]=cnt;
ss[cnt].push_back(y);
insert1(id[x],cnt);
}else
{
int idx=id[n]=id[x];
size[idx]++;
bool pd=false;
for(int i=0;i<(int)ss[idx].size();i++)
if(ss[idx][i]>y)
{
pd=true;
ss[idx].push_back(0);
for(int j=ss[idx].size()-1;j>i;j--) ss[idx][j]=ss[idx][j-1];
ss[idx][i]=y;
break;
}
if(!pd) ss[idx].push_back(y);
}
}
}
}
return 0;
}