1. 程式人生 > >BZOJ 3720 Gty的妹子樹 樹上分塊

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;
}