[Ynoi2008] stcm 题解
题目允许进行的 \(1\) 操作次数有限制,约为 \(n \log n\) 级别的,书上问题,于是考虑重链剖分的性质。
考虑单独对每一条重链进行操作。
假设集合 \(S\) 为我们当前的选中的这个点 \(x\) 的子树补信息。
对于这个点往下的重链,考虑往下走一步后,走到了 \(x_2\) ,对于 \(x_2\) 来说,他的子树补信息就等价于是 \(S + x + \sum_{y \in x, y \notin x_2} y\) 。
那么我们可以暴力的用 \(O(n)\)复杂度扫出重链上节点的子树补信息。
然后我们往儿子的重链上去继续扫,然后还有处理轻儿子的子树补信息。
先不考虑我们处理轻儿子的复杂度。
我们考虑我肯定让我这条重链的信息只会被增加一次,删除一次。
那么你考虑复杂度就是和 \(\text{dsu on tree}\) 类似了,子树和就是 \(n \log n\) 级别的。
考虑如何做轻儿子。
先考虑菊花图的情况,所有节点的父亲都是 \(1\) 。
这个时候你去求一个节点的子树补,那你会了,找到的集合就是一个节点除了他之外。
那直接建一颗线段树,考虑用线段树分治的方式,去左边的时候,就加上右边的贡献,然后去右边的时候加上左边的贡献。
每个节点被经过到根节点的深度次,然后总的是 \(n \log n\) 的。
那么你考虑往一般的情况扩展。
那一般情况不就是把轻儿子提出来然后建一颗线段树,然后直接分治就完了吗。
分治的时候到达了叶子节点,改标记节点为递归处理该节点所在重链。
但是删除,增添一个节点的复杂度并不是 \(O(1)\) 的,而是 \(O(siz)\) 的。
有点小问题。
于是总操作数量就是线段树的所有子树和,考虑转化一下形态。
所以这个线段树上的总操作就是 \(\sum_{x} dep_x \times siz_x\) ,考虑最小化这个代价容易想到,其实就是一个建立的哈夫曼树的过程。
那么对轻儿子建立哈夫曼树之后,复杂度根据经典结论这个式子的值就是 \(n \log n\) 的。
由于重链剖分对轻儿子规模的问题至少砍半。
于是总的操作次数就是 \(T(n) = T(n /2) + n \log n = 2 n \log n\) 。
可以通过构建递归树计算。
复杂度貌似是 \(n \log^2 n\) 的,不是很会分析,题解也没讲清楚,我后面研究一下再改。
由于我完全不会这题,是看了题解才会的,代码也和题解很像,而题解讲的足够详细,于是不发题解了((。
然后自己还写了一个对拍的小 \(\text{checker}\) 需要的可以私信。
代码只写了 \(\text{1.97 K}\) 。
代码放在这里了。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5;
int n, cnt, tot, fa[N], siz[N], son[N];
vector <int> ver[N], sons, lit;
priority_queue<pair<int,int> > q;
struct node {
int l, r;
node(int ll = 0,int rr = 0) { l = ll; r = rr;}
}a[N * 4];
void cleartree() {
for(int i = 1; i <= tot; i++) a[i].l = a[i].r = 0;
tot = 0; cnt = 0;
for(int i = 1; i <= n; i++) ver[i].clear(), siz[i] = 0, son[i] = 0, fa[i] = 0;
while(!q.empty()) q.pop();
}
void dfs(int x) {
siz[x] = 1;
for(auto to : ver[x]) {
dfs(to); siz[x] += siz[to];
if(siz[to] > siz[son[x]]) son[x] = to;
}
}
void add(int x) { cout << "+" << x; cnt++; }
void del() { cout << "-"; cnt-- ; }
void get(int x) { cout << "=" << x;}
void addson(int x) { add(x); for(auto to : ver[x]) addson(to); }
void dfs2(int x);
void dfs4(int x) {
if(!a[x].l || !a[x].r) { addson(x); return;}
dfs4(a[x].l), dfs4(a[x].r);
}
void dfs3(int x) {
if(!a[x].l || !a[x].r) { dfs2(x); return;}
int now = cnt;
dfs4(a[x].r), dfs3(a[x].l);
while(cnt > now) del();
dfs4(a[x].l), dfs3(a[x].r);
while(cnt > now) del();
}
void dfs2(int x) {
int u = x, now = cnt;
sons.clear(); lit.clear();
while(u) sons.emplace_back(u), u = son[u];
for(auto to : sons) {
get(to), add(to);
for(auto tt : ver[to])
if(tt != son[to]) addson(tt), lit.emplace_back(tt);
}
while(cnt > now) del();
if(!lit.size()) return;
for(auto to : sons) add(to);
for(auto to : lit) q.push(make_pair(-siz[to], to));
while(q.size() > 1) {
pair<int,int> u = q.top(); q.pop();
pair<int,int> v = q.top(); q.pop();
a[++tot] = node(u.second, v.second);
q.push(make_pair(u.first + v.first, tot));
}
int rt = q.top().second; q.pop();
dfs3(rt);
while(cnt > now) del();
}
signed main () {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> n) {
tot = n;
for(int i = 2; i <= n; i++){ cin >> fa[i]; ver[fa[i]].emplace_back(i); }
dfs(1); dfs2(1);
cleartree();
cout << "!\n";
}
return 0;
}