hdu 6394 Tree (树分块)

Tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 585    Accepted Submission(s): 206


 

Problem Description

Alice and Bob are playing with a magic tree This magic tree has n nodes,with n−1 magic paths connecting them into a connected block. Node 1 is at the top of the magic tree (layer 0). Node i is at the kth layer, where k is the distance from the node i to the node 1. Alice and Bob give a mana value on each node. If a magic stone falls on node i, it will be sent up to the k layer and appear on the kth ancestor node of the i layer(k is the mana value of node i). This node will continue to send up it, and so on. If the layer of node i is less than k, this stone will be sent out of the magic tree. Alice is curious, she will modify the magic value of a node, and ask Bob: If you drop a magic stone on the node x, how many times does it take to transfer it out of the magic tree?

 

Input

Input contains multiple tests
The first line contains one integer T(T≤4), indicating the number of test cases.
The following lines describe all the test cases
For each test case: The first line contains an integer n(n≤100000), indicating the size of the magic tree.
The second line has n−1 numbers, and the ith number represents the father of the node i+1.
The third row has n numbers, and the ith number represents the initial mana ai(ain) value of each node.
In the fourth line, a number m(m≤100000) represents the number of operations.
The next m lines, one operation per line.
First a number op(1≤op≤2) represents the type of operation.
If op==1, a number x will be read immediately, indicating that a magic stone is thrown to the node x.
If op==2, it will immediately read in two numbers x and new_a, indicating that the magic value of node x is modified to new_a(new_a≤n).

 

Output

For each query with op==1, output the answer

 

Sample Input

 

1

4

1 2 3

1 1 1 1

3

1 4

2 3 2

1 4

Sample Output

4

3

Hint

For the first query: 4-\>3-\>2-\>1-\>out For the second query:4-\>3-\>1-\>out

 

题目链接  

题意:有一颗树,每个节点有一个能量值k,可以将当前节点的东西向上一次传递k个距离。有两种操作,第一种是将一块石头放到第 i个节点上,问传递多少次使这块石头飞出这棵树。第二种操作是修改某个节点的k值。

解题方法:看着大佬的代码一边敲一边理解的。(嘤嘤嘤)

首先是树分块
1.dfs记录下每个节点的祖先节点(这里最多记录了20个)
2.通过bfs进行树分块,块大小P是sqrt(n)。按照bfs的遍历顺序,如果当前块的size小于P,那么当前遍历到的节点加入到当前的块中去,如果超过了P那么放到新的块中去
树分块主要记录的是对于当前节点 1.跳一次到达的节点t 2.跳多少次跳出块外cnt 3.跳出块外的下一个节点ans

树分块完成以后,

对于询问 ,只需要在ans上进行跳转,加入每次跳转的cnt,就可以输出答案

对于修改 ,先修改当前点的t,cnt,ans。然后在块内进行查询,把所有经过当前修改节点的cnt ,ans进行修改。

由于bfs的性质,块内的节点都是按照从上到下的顺序排列的。所以修改的时候可以在块内顺序遍历。

#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<typeinfo>
#include<string>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
using namespace std;

namespace fastIO {
#define BUF_SIZE 100000
	//fread -> read
	bool IOerror = 0;
	inline char nc() {
		static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
		if(p1 == pend) {
			p1 = buf;
			pend = buf + fread(buf, 1, BUF_SIZE, stdin);
			if(pend == p1) {
				IOerror = 1;
				return -1;
			}
		}
		return *p1++;
	}
	inline bool blank(char ch) {
		return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
	}
	inline void read(int &x) {
		char ch;
		while(blank(ch = nc()));
		if(IOerror) return;
		for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
	}
#undef BUF_SIZE
};
using namespace fastIO;

#define LL long long
#define ui unsigned int
#define uLL unsigned long long
#define scand(x) scanf("%d",&x)
#define scandd(x,y) scanf("%d%d",&x,&y)
#define scans(x) scanf("%s",x);
#define scanld(x) scanf("%lld",&x)
#define scanldd(x,y) scanf("%lld%lld",&x,&y)
#define rep(i,x,n) for(int i = x;i < n;i++)
#define dep(i,x,n) for(int i = x;i > n;i--)
#define pi 3.141592653589793238462643383249901429
#define mes(x) memset(x,0,sizeof(x))


const int maxn = 1e5 + 10;
int nxt[maxn],head[maxn],to[maxn],b[maxn],bel[maxn];
int tot,P,t[maxn],idx;
int f[maxn][20],fa[maxn],cnt[maxn],ans[maxn],vis[maxn];

void add(int x,int y)  //往树里面加边
{
	nxt[++tot] = head[x];
	head[x] = tot;
	to[tot] = y;
}

void dfs(int x)
{
	f[x][0] = fa[x];
	rep(i,1,20) f[x][i] = f[f[x][i - 1]][i - 1];
	for(int i = head[x];i;i = nxt[i]) 
		dfs(to[i]);
}

vector<int> maps[maxn];
queue<int> q;

int jmp(int x,int num)
{
	dep(i,19,-1)
	{
		if(num >= (1 << i))
		{
			num -=(1 << i);
			x = f[x][i];
		}
	}
	return x;
}

void bfs()
{
	while(!q.empty())
		q.pop();
	tot = 1;
	maps[tot].push_back(0);
	bel[0] = 1;
	int l = 0,r = 1;
	q.push(1);
	while(!q.empty()) 
	{
		if(fa[q.front()] != 0 && maps[bel[fa[q.front()]]].size() < P)
		{
			bel[q.front()] = bel[fa[q.front()]];
			maps[bel[q.front()]].push_back(q.front());
		}
		else
		{
			++tot;
			maps[tot].push_back(q.front());
			bel[q.front()] = tot;
		}
		int x = jmp(q.front(),b[q.front()]);

		if(bel[x] == bel[q.front()])
		{
			cnt[q.front()] = cnt[x] + 1;
			ans[q.front()] = ans[x];
		}
		else
		{
			cnt[q.front()] = 1;
			ans[q.front()] = x;
		}
		t[q.front()] = x;
		for(int i = head[q.front()];i;i = nxt[i])
		{
			q.push(to[i]);
		}
		q.pop();
	}
}

void Init()
{
	mes(ans);
	mes(head);
	mes(nxt);
	mes(to);
	mes(bel);
	mes(f);
	mes(fa);
	mes(cnt);
	mes(b);
	mes(vis);
	idx = 0;
	tot = 0;
}

int main()
{
	int T;
	int n;
	int temp;
	scand(T);
	while (T--)
	{
		Init();
		scand(n);
		P= sqrt(n) + 1;
		rep(i,0,n + 1)
			maps[i].clear();
		rep(i,2,n+1)
		{
			scand(fa[i]);
			add(fa[i],i);
		}
		rep(i,1,n + 1)
		{
			scand(b[i]);
		}
		dfs(1);
		bfs();
		int m;
		scand(m);
		while(m --)
		{
			int x,v;
			scand(temp);
			if(temp == 1)
			{
				scand(x);
				int sum = 0;
				while(x)
				{
					sum += cnt[x];
					x = ans[x];
				}
				printf("%d\n",sum);
			}
			else
			{
				scandd(x,v);
				b[x] = v;
				t[x] = jmp(x,b[x]);
				if(bel[x] == bel[t[x]])
				{
					cnt[x] = cnt[t[x]] + 1;
					ans[x] = ans[t[x]];
				}
				else
				{
					cnt[x] = 1;
					ans[x] = t[x];
				}
				++idx;
				vis[x] = idx;
				rep(i,0,maps[bel[x]].size())
				{
					int now = maps[bel[x]][i];
					if(vis[t[now]] == idx)
					{
						cnt[now] = cnt[t[now]] + 1;
						ans[now] = ans[t[now]];
						vis[now] = idx;
					}
				}
			}
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值