BZOJ 3720 Gty的妹子树 树上分块

题目大意:给出一棵树,要求维护:1.求出以x为根节点的子树的严格大于y的数量。

2.将一个节点的权值改变。

3.在一个节点下加一个权值为y的节点。

思路:分块这个东西太神了(别找我分析时间复杂度。。树上的分块更神。。。

首先,分块的原则和正常分块一样,把一棵树分成√n块,每一块不超过√n个,然后所有的时间复杂度降到了O(√n),(这个题还有个排序,所以还有一个log(n))。

如何在树上分块。规定根节点在编号为1的块中,然后做一次深搜,如果遍历到的一个节点的时候当前节点所在的块的数量已经达到最大的数量,那么就把遍历的子节点新建一个块,不然的话就把遍历的节点加到这个块中。

至于改值,怎么暴力怎么来,我直接改掉之后快排都能过。

加点的时候,分两种情况讨论,1.如果x节点所在块的数量还没有达到最大值,那就把y节点加进去,然后对整个序列快排。2.如果达到了最大的值,就新建一个块。

最后询问的时候,由于每一次操作之后块里存的数组都是有序的,因此查找只需要二分。写两个递归的Count函数,在不整的块中暴力查找,在整的块中二分查找。

CODE:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 30010
#define MAXP 10000
using namespace std;

struct Block{
	int num[1000],size;

	int Ask(int k) {
		int temp = upper_bound(num + 1,num + size + 1,k) - num - 1;
		return size - temp;
	}
}block[MAXP];

struct BlockGraph{
	int head[MAXP],total;
	int next[MAXP << 1],aim[MAXP << 1];

	void Add(int x,int y) {
		next[++total] = head[x];
		aim[total] = y;
		head[x] = total;
	}
}graph;

int points,asks,block_size,blocks;
int head[MAX << 1],total;
int next[MAX << 2],aim[MAX << 2];
int father[MAX << 1];

int src[MAX << 1],belong[MAX << 1];

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

void DFS(int x,int last)
{
	int from = belong[x];
	block[from].num[++block[from].size] = src[x];
	father[x] = last;
	for(int i = head[x]; i; i = next[i]) {
		if(aim[i] == last)	continue;
		if(block[from].size < block_size)
			belong[aim[i]] = from;
		else
			belong[aim[i]] = ++blocks;
		if(belong[aim[i]] != from)
			graph.Add(from,belong[aim[i]]);
		DFS(aim[i],x);
	}
}

int CountBlock(int x,int k)
{
	int re = block[x].Ask(k);
	for(int i = graph.head[x]; i; i = graph.next[i])
		re += CountBlock(graph.aim[i],k);
	return re;
}

int Count(int x,int k,int last)
{
	int re = src[x] > k;
	for(int i = head[x]; i; i = next[i]) {
		if(aim[i] == last)	continue;
		if(belong[x] == belong[aim[i]])	re += Count(aim[i],k,x);
		else	re += CountBlock(belong[aim[i]],k);
	}
	return re;
}

int main()
{
	cin >> points;
	for(int x,y,i = 1; i < points; ++i) {
		scanf("%d%d",&x,&y);
		Add(x,y),Add(y,x);
	}
	for(int i = 1; i <= points; ++i)
		scanf("%d",&src[i]);
	cin >> asks;
	block_size = (int)sqrt(points * log(points) / log(2));
	belong[1] = ++blocks;
	DFS(1,0);
	for(int i = 1; i <= blocks; ++i)
		sort(block[i].num + 1,block[i].num + block[i].size + 1);
	int last_ans = 0;
	for(int flag,x,y,i = 1; i <= asks; ++i) {
		scanf("%d%d%d",&flag,&x,&y);
		x ^= last_ans;
		y ^= last_ans;
		if(flag == 0)
			printf("%d\n",last_ans = Count(x,y,father[x]));
		else if(flag == 1) {
			static int from;
			from = belong[x];
			for(int i = 1; i <= block[from].size; ++i)
				if(block[from].num[i] == src[x]) {
					block[from].num[i] = src[x] = y;
					break;
				}
			sort(block[from].num + 1,block[from].num + block[from].size + 1);
		}
		else {
			static int from;
			from = belong[x];
			Add(x,++points);
			src[points] = y;
			father[points] = x;
			if(block[from].size < block_size) {
				block[from].num[++block[from].size] = y;
				sort(block[from].num + 1,block[from].num + block[from].size + 1);
				belong[points] = from;
			}
			else {
				graph.Add(from,++blocks);
				block[blocks].num[++block[blocks].size] = y;
				belong[points] = blocks;
			}
		}
	}
	return 0;
}

时间: 2024-10-10 00:51:00

BZOJ 3720 Gty的妹子树 树上分块的相关文章

BZOJ 3720: Gty的妹子树 [树上size分块]

传送门 题意: 一棵树,询问子树中权值大于$k$的节点个数,修改点权值,插入新点:强制在线 一开始以为询问多少种不同的权值,那道CF的强制在线带修改版,直接吓哭 然后发现看错了这不一道树上分块水题... 用王室联邦分块的话需要维护每一个块$dfs$序最小值和最大值,并且插入操作会破坏原来的性质 不如直接按$size$分块,根节点$size<block$就加入根,否则新建块 $size$分块不能保证块的数量,可以被菊花图卡掉,然而本题没有所以就可以安心的写了 每个块维护排序后的值 查询操作,不完整

BZOJ 3720 Gty的妹子树 块状树

题目大意:维护一棵树,每个点有一个权值,提供下列操作: 1.询问某棵子树中有多少个节点的权值大于x 2.修改某个节点的权值 3.增加一个叶子节点 强制在线 传说中的树分块 首先DFS,对于每个节点,如果这个节点的父亲节点所在块未满,就塞进父节点所在块中,否则自成一块,然后与父节点所在的块连边 添加节点同理 然后就按照分块直接搞吧0.0 细节实在是太多了 所以写挂的地方看看本蒟蒻的代码就好了0.0 #include <cmath> #include <cstdio> #include

luogu P2137 Gty的妹子树(分块,主席树)

询问的化我们可以建主席树.然后修改?,树套树...,最后插入?炸了. 所以我们对操作进行分块. 我们先对整棵树建一个主席树.修改,插入我们先记录下来.然后询问的时候先对主席树查询,然后暴力遍历我们记录下来的修改插入操作.每\(\sqrt{m}\)次操作后我们重新构建一个主席树.这样我们保证了重建主席树和询问的总复杂度为\(O(nlogn\sqrt{m})\)然后就把这道题解决了. 有一个难办的事就是如何记录修改和插入的操作.可以使每次询问的时候我们可以知道修改和插入是否在\(u\)的子树中以便我

【BZOJ3720】Gty的妹子树 块状树

[BZOJ3720]Gty的妹子树 我曾在弦歌之中听过你,檀板声碎,半出折子戏.舞榭歌台被风吹去,岁月深处尚有余音一缕……Gty神(xian)犇(chong)从来不缺妹子……他来到了一棵妹子树下,发现每个妹子有一个美丽度……由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣.他想知道某个子树中美丽度大于k的妹子个数.某个妹子的美丽度可能发生变化……树上可能会出现一只新的妹子……维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi.支持以下操作:0 u x 

bzoj 3744: Gty的妹子序列 主席树+分块

3744: Gty的妹子序列 Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 101  Solved: 34[Submit][Status] Description 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakse

BZOJ 3720: Gty的妹子树

3720: Gty的妹子树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1493  Solved: 502[Submit][Status][Discuss] Description 我曾在弦歌之中听过你, 檀板声碎,半出折子戏. 舞榭歌台被风吹去, 岁月深处尚有余音一缕…… Gty神(xian)犇(chong)从来不缺妹子…… 他来到了一棵妹子树下,发现每个妹子有一个美丽度…… 由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣. 他想知道

BZOJ 3744: Gty的妹子序列

3744: Gty的妹子序列 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1335  Solved: 379[Submit][Status][Discuss] Description 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹

bzoj 1086: [SCOI2005]王室联邦(树上分块)

1086: [SCOI2005]王室联邦 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge Submit: 1107  Solved: 662 [Submit][Status][Discuss] Description "余"人国的国王想重新编制他的国家.他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成 员来管理.他的国家有n个城市,编号为1..n.一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条

BZOJ 3731 3731: Gty的超级妹子树 [树上size分块 !]

传送门 题意:一棵树,询问子树中权值大于k的节点个数,修改点权值,插入新点,断开边:强制在线 该死该死该死!!!!!! MD我想早睡觉你知不知道 该死该死沙比提 断开边只会影响一个块,重构这个块就行了 如果断开的点$u$是这个块$p$的根,只修改原图和块图就好了 否则,把$u$子树在块中的部分从$p$里删除,放到一个新块里.并且,这些点连到的其他块的点,也要在块图上与$p$断开与新块相连 所以我们维护一个删除边的$mark$标记 WA:一开始原图加的是双向边,通过判断$fa$防止出错..后来$f