#BZOJ3720 GTY的妹子树

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

现充爆炸吧!!!╭(╯^╰)╮

咳咳……树分块,简单说明一下分块方式:

对于一个节点,我们检查它父节点所在块的大小是否小于size,是就加入父节点所在块,否则新开一块。严格保证直径、大小、连通性,不保证块的个数。树上一般多用这种分块方式,当然也可以按dfs序分块,可以保证块的大小,也比较好写,这里不多讲dfs序分块方法。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 6e4+10;
int n,m,siz,las,a[N];
int Count,bl[N],fa[N];//所在块标号
vector<int>vec[N];//块内元素
struct edge_Node{//vector要被卡……bsoj数据毒瘤~还没O2优化 
	int cnt,h[N],next[N<<1],to[N<<1];
	inline void add(int u,int v){
		next[++cnt]=h[u];
		to[cnt]=v;
		h[u]=cnt;
	}
}G,v;
#define Gto G.to[p]
#define Gnext G.next[p]
#define vto v.to[p]
#define vnext v.next[p]
struct Tree{
	inline void init(){
		scanf("%d",&n);
		Inc(i,1,n-1){
			int x,y;scanf("%d%d",&x,&y);
			G.add(x,y);
			G.add(y,x);
		}
		siz=400;//注意此处√n会被卡掉,设静态size值(也算个技巧吧) 
		Inc(i,1,n)scanf("%d",a+i);
		vec[bl[1]=++Count].push_back(a[1]);//初始化 
	}
	inline void dfs(int x,int Fa){
		for(int p=G.h[x];p;p=Gnext)if(Gto^Fa){
			vec[bl[x]].size()>=siz?v.add(bl[x],bl[Gto]=++Count),vec[bl[Gto]].push_back(a[Gto]):vec[bl[Gto]=bl[x]].push_back(a[Gto]);
			dfs(Gto,fa[Gto]=x);
		}
	}
}t;
inline void init(){//暴力排序 
	Inc(i,1,Count)sort(vec[i].begin(),vec[i].end());//n^(3/2)
	scanf("%d",&m);
}
inline int dfs2(int idx,int lim){//跳块 ,n^(1/2)logn^(1/2)
	int ret=vec[idx].size()-(upper_bound(vec[idx].begin(),vec[idx].end(),lim)-vec[idx].begin());
	for(int p=v.h[idx];p;p=vnext)ret+=dfs2(vto,lim);
	return ret;
}
inline int dfs1(int x,int lim){
	int ret=a[x]>lim,idx=bl[x];
	for(int p=G.h[x];p;p=Gnext)if(Gto^fa[x]){
		bl[Gto]^idx?ret+=dfs2(bl[Gto],lim):ret+=dfs1(Gto,lim);//第一个块视为散块直接暴力,其它块直接跳起 
	}return ret;
}
inline int Query(int x,int lim){
	return las=dfs1(x,lim);
}
inline void Change(int x,int k){
	int idx=bl[x];
	vec[idx].erase(lower_bound(vec[idx].begin(),vec[idx].end(),a[x]));
	vec[idx].insert(lower_bound(vec[idx].begin(),vec[idx].end(),k),a[x]=k);//暴力插入 
}
inline void Insert(int x,int k){
	G.add(fa[++n]=x,n+1);
	if(vec[bl[x]].size()<siz){
		vec[bl[n]=bl[x]].insert(lower_bound(vec[bl[x]].begin(),vec[bl[x]].end(),k),a[n]=k);
	}else {
		v.add(bl[x],bl[n]=++Count);
		vec[bl[n]].push_back(a[n]=k);
	}
}
inline void solv(){
	while(m--){
		int op,u,x;scanf("%d%d%d",&op,&u,&x);u^=las,x^=las;
		if(op==0)cout<<Query(u,x)<<"\n";
		else if(op==1)Change(u,x);
		else if(op==2)Insert(u,x);
	}
}
int main(){
//	freopen("Gty的妹子树.in","r",stdin);
//	freopen("Gty的妹子树.out","w",stdout);
	t.init();
	t.dfs(1,0);
	init();
	solv();
	return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值