[BZOJ 4771] 七彩树

14 篇文章 0 订阅
8 篇文章 0 订阅
BZOJ传送门

题目描述

给定一棵 n n n个点的有根树,编号依次为 1 1 1 n n n,其中 1 1 1号点是根节点。每个节点都被染上了某一种颜色,其中第 i i i个节点的颜色为 c [ i ] c[i] c[i]。如果 c [ i ] = c [ j ] c[i]=c[j] c[i]=c[j],那么我们认为点 i i i和点 j j j拥有相同的颜色。定义 d e p t h [ i ] depth[i] depth[i] i i i节点与根节点的距离,为了方便起见,你可以认为树上相邻的两个点之间的距离为 1 1 1。站在这棵色彩斑斓的树前面,你将面临 m m m个问题。

每个问题包含两个整数 x x x d d d,表示询问 x x x子树里且 d e p t h depth depth不超过 d e p t h [ x ] + d depth[x]+d depth[x]+d的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。

输入输出格式

####输入格式

第一行包含一个正整数 T ( 1 ≤ T ≤ 500 ) T(1\leq T\leq 500) T(1T500),表示测试数据的组数。

每组数据中,第一行包含两个正整数 n ( 1 ≤ n ≤ 100000 ) n(1\leq n\leq 100000) n(1n100000) m ( 1 ≤ m ≤ 100000 ) m(1\leq m\leq 100000) m(1m100000),表示节点数和询问数。

第二行包含 n n n个正整数,其中第 i i i个数为 c [ i ] ( 1 ≤ c [ i ] ≤ n ) c[i](1\leq c[i]\leq n) c[i](1c[i]n),分别表示每个节点的颜色。

第三行包含 n − 1 n-1 n1个正整数,其中第 i i i个数为 f [ i + 1 ] ( 1 ≤ f [ i ] ) f[i+1](1\leq f[i]) f[i+1](1f[i])

接下来 m m m行,每行两个整数 x ( 1 ≤ x ≤ n ) x(1\leq x\leq n) x(1xn) d ( 0 ≤ d ) d(0\leq d) d(0d)

输入数据经过了加密,对于每个询问,如果你读入了 x x x d d d,那么真实的 x x x d d d分别是 x   x o r   l a s t x \ xor\ last x xor last d   x o r   l a s t d\ xor\ last d xor last

其中 l a s t last last表示这组数据中上一次询问的答案,如果这是当前数据的第一组询问,那么 l a s t = 0 last=0 last=0

输入数据保证 n n n m m m的总和不超过 500000 500000 500000

####输出格式

对于每个询问输出一行一个整数,即答案。

输入输出样例

输入样例#1
1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
输出样例
1
2
3
1
1
2
1
1

解题分析

首先考虑没有深度的限制怎么做。 显然我们可以将树上的问题转化为序列上的问题, 子树内的 D F S DFS DFS序是连续的一段, 所以直接线段树维护。 去重也很简单, 设 A A A B B B点颜色相同且在同色点中 D F S DFS DFS序相邻, 那么在 A , B A,B A,B点贡献分别 + 1 +1 +1 L C A ( A , B ) LCA(A,B) LCA(A,B)处贡献 − 1 -1 1即可。

现在有了深度的限制, 但没有修改。 我们可以利用主席树一层一层加入点, 用 s e t set set维护同色点 D F S DFS DFS序的情况, 方便找到前驱后继。 最后答案直接在相应层的根节点向下寻找。

很坑的一点: 树剖 L C A LCA LCA的重儿子数组 s o n son son是需要清零的…莫名 R E RE RE 20分钟QAQ…

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <vector>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 100050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dot, q, arr, cnt;
int head[MX], root[MX], col[MX], son[MX],
fat[MX], dep[MX], topf[MX], siz[MX], lb[MX], rb[MX];
struct Edge {int to, nex;} edge[MX << 1];
struct Node {int sum, son[2];} tree[MX << 7];
struct INFO {int id;};
IN bool operator < (const INFO &x, const INFO &y) {return lb[x.id] < lb[y.id];}
std::set <INFO> st[MX];
std::vector <int> dp[MX];
IN void add(R int from, R int to)
{edge[++cnt] = {to, head[from]}, head[from] = cnt;}
namespace LCA
{
	void DFS(R int now)
	{
		lb[now] = ++arr; siz[now] = 1; dp[dep[now]].push_back(now);
		for (R int i = head[now]; i; i = edge[i].nex)
		{
			if(edge[i].to == fat[now]) continue;
			dep[edge[i].to] = dep[now] + 1;
			fat[edge[i].to] = now;
			DFS(edge[i].to);
			siz[now] += siz[edge[i].to];
			if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
		}
		rb[now] = arr;
	}
	void DFS(R int now, R int grand)
	{
		topf[now] = grand;
		if(!son[now]) return;
		DFS(son[now], grand);
		for (R int i = head[now]; i; i = edge[i].nex)
		{
			if(edge[i].to == fat[now] || edge[i].to == son[now]) continue;
			DFS(edge[i].to, edge[i].to);
		}
	}
	IN int query(R int x, R int y)
	{
		W (topf[x] != topf[y])
		{
			if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y);
			x = fat[topf[x]];
		}
		return dep[x] < dep[y] ? x : y;
	}
}
namespace SGT
{
	#define ls tree[now].son[0]
	#define rs tree[now].son[1]
	IN void nw(R int now) {ls = rs = tree[now].sum = 0;}
	void merge(int &now, R int pre, R int lef, R int rig)
	{
		if(!(1ll * now * pre)) return now = now + pre, void();
		tree[now].sum += tree[pre].sum;
		if(lef == rig) return;
		int mid = lef + rig >> 1;
		merge(ls, tree[pre].son[0], lef, mid);
		merge(rs, tree[pre].son[1], mid + 1, rig);
	}
	void modify(int &now, R int lef, R int rig, R int tar, R int del)
	{
		if(!now) now = ++arr, nw(arr);
		tree[now].sum += del;
		if(lef == rig) return;
		int mid = lef + rig >> 1;
		if(tar <= mid) modify(ls, lef, mid, tar, del);
		else modify(rs, mid + 1, rig, tar, del);
	}
	int query(R int now, R int lef, R int rig, R int lb, R int rb)
	{
		if(lef >= lb && rig <= rb) return tree[now].sum;
		int ret = 0, mid = lef + rig >> 1;
		if(lb <= mid) ret += query(ls, lef, mid, lb, rb);
		if(rb > mid) ret += query(rs, mid + 1, rig, lb, rb);
		return ret;
	}
	#undef ls
	#undef rs
}
void reset()
{
	std::memset(head, cnt = 0, sizeof(head));
	dep[1] = 1;
	std::memset(root, arr = 0, sizeof(root));
	std::memset(son, 0, sizeof(son));//清零, 否则第二次DFS的时候出锅
	for (R int i = 1; i <= dot; ++i) st[i].clear(), dp[i].clear();
}
void solve()
{
	int a, b, sz, now, mx = 99999999, last = 0;
	std::set <INFO>::iterator it1, it2;
	in(dot), in(q);
	for (R int i = 1; i <= dot; ++i) in(col[i]);
	for (R int i = 2; i <= dot; ++i)
	in(a), add(a, i), add(i, a);
	LCA::DFS(1), LCA::DFS(1, 1);
	for (R int i = 1; i <= dot; ++i)
	{
		sz = dp[i].size(); if(!sz) {mx = i - 1; break;}
		for (R int j = 0; j < sz; ++j)
		{
			now = dp[i][j];
			it1 = it2 = st[col[now]].lower_bound({now});
			if(it2 == st[col[now]].begin() || !st[col[now]].size())//注意空的情况
			it1 = st[col[now]].end();
			else it1--;
			if(it1 != st[col[now]].end() && it2 != st[col[now]].end())
			SGT::modify(root[i], 1, dot, lb[LCA::query(it1 -> id, it2 -> id)], 1);
			if(it1 != st[col[now]].end())
			SGT::modify(root[i], 1, dot, lb[LCA::query(it1 -> id, now)], -1);
			if(it2 != st[col[now]].end())
			SGT::modify(root[i], 1, dot, lb[LCA::query(now, it2 -> id)], -1);
			SGT::modify(root[i], 1, dot, lb[now], 1);
			st[col[now]].insert({now});
		}
		SGT::merge(root[i], root[i - 1], 1, dot);
	} mx = std::min(mx, dot);
	W (q--)
	{
		in(a), in(b);
		a ^= last, b ^= last;
		b = std::min(mx, dep[a] + b);
		printf("%d\n", last = SGT::query(root[b], 1, dot, lb[a], rb[a]));
	}
}
int main(void)
{
	int T;
	in(T);
	W (T--) reset(), solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值