块状树(bzoj 3720: Gty的妹子树)

问题概述:

一棵n个节点的树,每个节点都有一个权值,之后三种操作:

①0 u v查询以u点为根的子树中权值大于v的有多少个

②1 u v表示将u点的权值修改为v

③2 u v表示新建一个节点,编号为节点个数+1,挂在节点u上面,权值为v

强制在线,所有操作中的u和v异或上一次的答案


块状树其实就是将树分块

分块方法:DFS,对于当前节点,如果这个节点的父亲所在块还没有满,就将这个点归入父亲所在块中,否则新建一个块,然后父亲所在的块和这个新建的块连一条边,最后可以形成一棵块状树(默认块大小sqrt(n))


对于这道题,先进行树分块,每个块存一个序列,这个序列就是块中所有点的权值从小到大排列形成

①查询操作0 u v:和u点在一个块里的点直接爆搜,因为点数不超过sqrt(n),所以复杂度就是O(sqrt(n)),而u点子树中所有属于其他块的点,就可以直接在块里二分了,复杂度O(sqrt(n)logn)

②修改操作1 u v:修改它所在块的序列,可以直接暴力重新排序,复杂度O(sqrt(n)log(sqrt(n)))

③加点操作2 u v:和建树的过程一样,判断这个点的父亲u所在块有没有满,有就加入并更新(还是暴力重新序),没有就新建

其实在极大部分情况,每个块的大小都差不多sqrt(n)左右,但是最差情况每个块的大小甚至只有1,也就是说会有n块,这个时候复杂度会降为O(n²)甚至更高,所以这个方法还是慎用


3720: Gty的妹子树

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1863   Solved: 640
[ Submit][ Status][ Discuss]

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


这题就是上面那个例子

#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> G[60005], C[5000];
int B, cnt, val[60005], fa[60005], bel[60005], c[5000][5000], h[5000];
void Sech(int u, int p)
{
	int i, v, now;
	fa[u] = p;
	if(u!=1 && h[bel[p]]>=B)
	{
		now = ++cnt;
		C[bel[p]].push_back(now);
	}
	else
		now = bel[p];
	if(u!=1)
	{
		h[now]++, c[now][h[now]] = val[u];
		bel[u] = now;
	}
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(v==p)
			continue;
		Sech(v, u);
	}
}
int SechC(int u, int now)
{
	int i, v, ans;
	ans = c[u]+h[u]+1-upper_bound(c[u]+1, c[u]+h[u]+1, now);
	for(i=0;i<C[u].size();i++)
	{
		v = C[u][i];
		ans += SechC(v, now);
	}
	return ans;
}
int La(int u, int now)
{
	int i, v, ans;
	ans = 0;
	if(val[u]>now)
		ans++;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(v==fa[u])
			continue;
		if(bel[v]==bel[u])
			ans += La(v, now);
		else
			ans += SechC(bel[v], now);
	}
	return ans;
}
int main(void)
{
	int n, i, x, now, y, ans, opt, Q;
	scanf("%d", &n);
	B = sqrt(n+30000)+1;
	for(i=1;i<=n-1;i++)
	{
		scanf("%d%d", &x, &y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	for(i=1;i<=n;i++)
		scanf("%d", &val[i]);
	cnt = bel[1] = h[1] = 1;
	c[1][1] = val[1];
	Sech(1, 0);
	for(i=1;i<=cnt;i++)
		sort(c[i]+1, c[i]+h[i]+1);
	ans = 0;
	scanf("%d", &Q);
	while(Q--)
	{
		scanf("%d%d%d", &opt, &x, &y);
		x ^= ans, y ^= ans;
		if(opt==0)
		{
			ans = La(x, y);
			printf("%d\n", ans);
		}
		else if(opt==1)
		{
			now = bel[x];
			c[now][lower_bound(c[now]+1, c[now]+h[now]+1, val[x])-c[now]] = y;
			sort(c[now]+1, c[now]+h[now]+1);
			val[x] = y;
		}
		else
		{
			n += 1;
			val[n] = y;
			fa[n] = x;
			G[n].push_back(x);
			G[x].push_back(n);
			if(h[bel[x]]>=B)
			{
				now = ++cnt;
				C[bel[x]].push_back(now);
			}
			else
				now = bel[x];
			h[now]++, c[now][h[now]] = val[n];
			bel[n] = now;
			sort(c[now]+1, c[now]+h[now]+1);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值