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(1≤T≤500),表示测试数据的组数。
每组数据中,第一行包含两个正整数 n ( 1 ≤ n ≤ 100000 ) n(1\leq n\leq 100000) n(1≤n≤100000)和 m ( 1 ≤ m ≤ 100000 ) m(1\leq m\leq 100000) m(1≤m≤100000),表示节点数和询问数。
第二行包含 n n n个正整数,其中第 i i i个数为 c [ i ] ( 1 ≤ c [ i ] ≤ n ) c[i](1\leq c[i]\leq n) c[i](1≤c[i]≤n),分别表示每个节点的颜色。
第三行包含 n − 1 n-1 n−1个正整数,其中第 i i i个数为 f [ i + 1 ] ( 1 ≤ f [ i ] ) f[i+1](1\leq f[i]) f[i+1](1≤f[i])
接下来 m m m行,每行两个整数 x ( 1 ≤ x ≤ n ) x(1\leq x\leq n) x(1≤x≤n)和 d ( 0 ≤ d ) d(0\leq d) d(0≤d)
输入数据经过了加密,对于每个询问,如果你读入了 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();
}