Solution Set【2024.1.8】
A. 水题
考虑根号重构。
对于块内的询问,发现其根链可以分为两部分,一部分为这组操作前的根链,另一部分为被这组操作内的修改操作影响的根链。考虑如何维护。
发现问题在于我们要在 \(\mathcal{O}(n)\) 的时间复杂度内查询 \(\sqrt{n}\) 个链的答案。将所有查询离线储存在节点上,然后 dfs 该树,在 dfs 过程中维护当前根链中某个颜色出现的最深位置是哪个,这样在遍历到询问节点时可以快速的查询其一部分的答案,至于被这组操作内的修改操作影响的根链,将所有修改操作遍历一遍并维护出会影响答案的操作在遍历到节点的时候将其颜色插入到根链中即可。
现在问题转化为了需要一个 \(\mathcal{O}(1)\) 修改,\(\mathcal{O}(\sqrt{n})\) 查询可重集合内某个值域范围内的数的数量的数据结构,使用值域分块即可。
至于处理完成每组操作后如何将修改操作更改到树中,可以考虑按时间序逆序枚举操作,对于每次修改操作,暴力跳父节点直到走到根节点或走到一个被这组内的操作修改过的节点,不难发现每个节点最多被修改 \(1\) 次,因此这部分的复杂度为 \(\mathcal{O}(n)\)。
总复杂度为 \(\mathcal{O}(n \sqrt{n})\)。
Code
#include <bits/stdc++.h>
typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
class RangeBlock {
private:
valueType N, B, K;
ValueVector leftBound, rightBound, count;
ValueVector belong, data;
public:
RangeBlock() = default;
RangeBlock(valueType n) : N(n), B(2 * std::sqrt(n)), K((N + B - 1) / B) {
leftBound.resize(K + 1);
rightBound.resize(K + 1);
count.resize(K + 1);
belong.resize(N + 1);
data.resize(N + 1);
for (valueType i = 1; i <= K; ++i) {
leftBound[i] = (i - 1) * B + 1;
rightBound[i] = i * B;
}
rightBound[K] = N;
for (valueType i = 1; i <= K; ++i)
for (valueType j = leftBound[i]; j <= rightBound[i]; ++j)
belong[j] = i;
}
void resize(valueType n) {
N = n;
B = 2 * std::sqrt(n);
K = (N + B - 1) / B;
leftBound.resize(K + 1);
rightBound.resize(K + 1);
count.resize(K + 1);
belong.resize(N + 1);
data.resize(N + 1);
for (valueType i = 1; i <= K; ++i) {
leftBound[i] = (i - 1) * B + 1;
rightBound[i] = i * B;
}
rightBound[K] = N;
for (valueType i = 1; i <= K; ++i)
for (valueType j = leftBound[i]; j <= rightBound[i]; ++j)
belong[j] = i;
}
void clear() {
std::fill(count.begin(), count.end(), 0);
std::fill(data.begin(), data.end(), 0);
}
public:
void Insert(valueType x) {
++count[belong[x]];
++data[x];
}
void Erase(valueType x) {
--count[belong[x]];
--data[x];
}
valueType query(valueType l, valueType r) {
if (l > r)
return 0;
valueType ans = 0;
if (belong[l] == belong[r]) {
for (valueType i = l; i <= r; ++i)
ans += data[i];
return ans;
}
for (valueType i = belong[l] + 1; i <= belong[r] - 1; ++i)
ans += count[i];
for (valueType i = l; i <= rightBound[belong[l]]; ++i)
ans += data[i];
for (valueType i = leftBound[belong[r]]; i <= r; ++i)
ans += data[i];
return ans;
}
};
valueType N, Q;
ValueVector color;
ValueVector father, timeStamp, size, son, top, depth;
ValueVector leftBound, rightBound, node;
RangeBlock block;
ValueMatrix G;
void initDfs(valueType x) {
size[x] = 1;
depth[x] = depth[father[x]] + 1;
for (auto const &to : G[x]) {
initDfs(to);
size[x] += size[to];
if (son[x] == 0 || size[to] > size[son[x]])
son[x] = to;
}
}
void build(valueType x, valueType _top, valueType &dfsCount) {
top[x] = _top;
leftBound[x] = ++dfsCount;
node[dfsCount] = x;
if (son[x] != 0)
build(son[x], _top, dfsCount);
for (auto const &to : G[x]) {
if (to == son[x])
continue;
build(to, to, dfsCount);
}
rightBound[x] = dfsCount;
}
valueType lca(valueType x, valueType y) {
while (top[x] != top[y]) {
if (depth[top[x]] < depth[top[y]])
std::swap(x, y);
x = father[top[x]];
}
if (depth[x] > depth[y])
std::swap(x, y);
return x;
}
bool InTree(valueType u, valueType v) {// check if v in the subtree of u
return leftBound[u] <= leftBound[v] && rightBound[v] <= rightBound[u];
}
void TestCaseClear() {
N = 0;
Q = 0;
color.clear();
father.clear();
timeStamp.clear();
size.clear();
son.clear();
top.clear();
depth.clear();
leftBound.clear();
rightBound.clear();
node.clear();
G.clear();
}
PairMatrix ancestor, task;
ValueVector last, replace;
void dfs(valueType x) {
replace[x] = last[color[x]];
last[color[x]] = x;
block.Insert(depth[x]);
if (replace[x] != 0)
block.Erase(depth[replace[x]]);
for (auto &[id, count] : task[x]) {
valueType max = 0;
ValueVector pool;
for (auto const &[u, c] : ancestor[id]) {
max = std::max(max, depth[u]);
if (last[c] != 0) {
pool.push_back(last[c]);
block.Erase(depth[last[c]]);
last[c] = 0;
}
block.Insert(depth[x] + 1);
}
count = block.query(max + 1, depth[x] + 1);
for (auto const &u : pool) {
last[color[u]] = u;
block.Insert(depth[last[color[u]]]);
}
for (auto const &iter : ancestor[id])
block.Erase(depth[x] + 1);
}
for (auto const &to : G[x])
dfs(to);
block.Erase(depth[x]);
if (replace[x] != 0)
block.Insert(depth[replace[x]]);
last[color[x]] = replace[x];
}
void GroupClear() {
std::fill(last.begin(), last.end(), 0);
std::fill(replace.begin(), replace.end(), 0);
std::fill(ancestor.begin(), ancestor.end(), PairVector());
std::fill(task.begin(), task.end(), PairVector());
block.clear();
}
void Init() {
color.resize(N + 1);
father.resize(N + 1);
timeStamp.resize(N + 1);
size.resize(N + 1);
son.resize(N + 1);
top.resize(N + 1);
depth.resize(N + 1);
leftBound.resize(N + 1);
rightBound.resize(N + 1);
node.resize(N + 1);
G.resize(N + 1);
block.resize(N + 1);
ancestor.resize(Q + 1);
task.resize(N + 1);
last.resize(N + 1);
replace.resize(N + 1);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
#ifndef LOCAL_STDIO
freopen("simple.in", "r", stdin);
freopen("simple.out", "w", stdout);
#endif
valueType T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
TestCaseClear();
std::cin >> N >> Q;
Init();
// valueType const B = std::ceil(std::sqrt((double) Q));
constexpr valueType B = 950;
valueType const K = (Q + B - 1) / B;
ValueVector leftBound(K + 1), rightBound(K + 1);
for (valueType i = 1; i <= K; ++i) {
leftBound[i] = (i - 1) * B;
rightBound[i] = i * B;
}
rightBound[K] = Q;
TupleVector query(Q);
ValueVector ans(Q);
for (valueType i = 2; i <= N; ++i) {
std::cin >> father[i];
G[father[i]].push_back(i);
}
for (valueType i = 1; i <= N; ++i)
std::cin >> color[i];
valueType dfsCount = 0;
initDfs(1);
build(1, 1, dfsCount);
for (auto &[type, u, c] : query) {
std::cin >> type;
if (type == 1)
std::cin >> u >> c;
else
std::cin >> u;
}
bitset exist(N + 1, false);
for (valueType k = 1; k <= K; ++k) {
GroupClear();
for (valueType i = leftBound[k]; i < rightBound[k]; ++i) {
auto const &[type, u, c] = query[i];
if (type == 1)
continue;
task[u].emplace_back(i, 0);
for (valueType j = leftBound[k]; j < i; ++j) {
auto const &[jType, jU, jC] = query[j];
if (jType == 2)
continue;
valueType const lcaU = lca(u, jU);
while (!ancestor[i].empty() && InTree(ancestor[i].back().first, lcaU)) {
exist[ancestor[i].back().second] = false;
ancestor[i].pop_back();
}
if (!exist[jC])
ancestor[i].emplace_back(lcaU, jC);
exist[jC] = true;
}
for (auto const &[lcaU, jC] : ancestor[i])
exist[jC] = false;
}
dfs(1);
for (valueType i = leftBound[k]; i < rightBound[k]; ++i) {
auto const &[type, u, c] = query[i];
if (type == 2) {
auto const [id, data] = task[u].back();
task[u].pop_back();
ans[id] = data;
}
}
for (valueType i = rightBound[k] - 1; i >= leftBound[k]; --i) {
auto const &[type, u, c] = query[i];
if (type == 2)
continue;
valueType x = u;
while (x != 0 && timeStamp[x] != k) {
color[x] = c;
timeStamp[x] = k;
x = father[x];
}
}
}
for (valueType i = 0; i < Q; ++i)
if (std::get<0>(query[i]) == 2)
std::cout << ans[i] << '\n';
}
std::cout << std::flush;
return 0;
}