[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;
}
posted @ 2021-11-10 15:47  Pitiless0514  阅读(80)  评论(2编辑  收藏  举报