BZOJ 3731: Gty的超级妹子树

31 篇文章 0 订阅
5 篇文章 0 订阅

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 &lt; l i m size&lt;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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值