bzoj4754 [JSOI2016]独特的树叶

bzoj4754 [JSOI2016]独特的树叶

给定两棵无根树 \(A,\ B\) ,其中 \(B\) 恰好是由 \(A\) 添加一个叶子,然后将节点的编号打乱得到的,求这个被添加的叶子在 \(B\) 中的编号(若有多个符合要求的叶子,输出编号最小的)。

\(|A|\leq10^5\)

hash,dp,二次换根


考虑使用树hash来快速比较,我以前用的hash式子是 \(hash_u=(\displaystyle\sum_{v\in \text{son(u)}}base^{k-i-1}\times hash_u+hash_v)+1\) ,但这样错误率很高(很容易有两个形态不同的树hash值相同),可以考虑将式子中的 \(1\) 以与树的形态有关的值替代,这道题可以使用 \(\text{size(u)}\)

可以考虑求出 \(A,\ B\) 以每个节点为根时的 hash 值,并枚举 \(B\) 的所有叶子,并快速求出去掉该叶子后整棵树的hash值。按照上面的式子,若以叶子 \(u\) 为根的hash值为 \(x\) ,那么去掉 \(u\) 后整棵树的hash值为 \(x-|B|\)

我们可以 \(O(n\log n)\) 求出每个点子树的hash值,记为 \(f_i\) ,记 \(g_i\) 为以 \(i\) 为根的hash值,可以考虑使用二次换根(自顶向下)。

令当前节点为 \(v\) ,该节点的父亲为 \(u\)\(g_v\) 相当于 \(f_v\) 多了一个 “以 \(u\) 为根去掉儿子 \(v\) 的hash值” 的儿子,暴力枚举 \(u\) 为根时的所有儿子可以被菊花图卡掉,因此需要化一下式子。可以记录下以每个点为根时的所有儿子的hash值 \(val_{u,\ i}\) ,并维护这时的 “hash前缀和” \(sum_{u,\ k}=\displaystyle\sum_{i=0}^kval_{u,\ i}\times base^{n-i-1}\) 。“以 \(u\) 为根去掉儿子 \(v\) 的hash值” 就相当于求一个序列删掉一个点后的hash值。令 \(n=\text{size(}val_u\text{)}\) ,去掉位置 \(k\) 后的hash值即为 \(sum_{u,\ n}-(sum_{u,\ k}-sum_{u,\ k-1})\times base^{n-k}\) 。求出 “以 \(u\) 为根去掉儿子 \(v\) 的hash值” 后扫一遍 \(v\) 的儿子就可以处理出 \(g_v,\ val_v,\ sum_v\) 了。时间复杂度 \(O(n\log n)\)

可以发现hash式子中的最后一项 \(\text{size(u)}\) 既满足错误率低,又满足转移方便,因此这是树hash的一个不错的选择(雾

时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long u64;
const int maxn = 1e5 + 10;
const u64 base = 19260817, Inv = 7089841341079321457ull;
int n;
u64 pw[maxn];

struct Tree {
  int N, sz[maxn];
  u64 dp1[maxn], dp2[maxn];

  vector <int> e[maxn];
  vector <u64> vec[maxn], sum[maxn];

  u64 dfs1(int u, int f) {
    sz[u] = 1;
    for (int v : e[u]) {
      if (v != f) vec[u].push_back(dfs1(v, u)), sz[u] += sz[v];
    }
    sort(vec[u].begin(), vec[u].end());
    int SZ = vec[u].size(); sum[u].resize(SZ);
    for (int i = 0; i < SZ; i++) {
      sum[u][i] = (i ? base * sum[u][i - 1] : 0) + vec[u][i];
    }
    return dp1[u] = (SZ ? sum[u].back() : 0) + sz[u];
  }

  void dfs2(int u, int f) {
    int SZ = vec[f].size();
    int pos = distance(vec[f].begin(), lower_bound(vec[f].begin(), vec[f].end(), dp1[u]));
    vec[u].push_back(sum[f][SZ - 1] - (sum[f][pos] - (pos ? sum[f][pos - 1] : 0)) * pw[SZ - pos - 1] + N - sz[u]);
    sort(vec[u].begin(), vec[u].end());
    SZ = vec[u].size(), sum[u].resize(SZ);
    for (int i = 0; i < SZ; i++) {
      sum[u][i] = (i ? base * sum[u][i - 1] : 0) + vec[u][i];
    }
    dp2[u] = sum[u].back() + N;
    for (int v : e[u]) {
      if (v != f) dfs2(v, u);
    }
  }

  void build() {
    for (int i = 1; i < N; i++) {
      int u, v;
      scanf("%d %d", &u, &v);
      e[u].push_back(v), e[v].push_back(u);
    }
    dfs1(1, 0);
    dp2[1] = dp1[1];
    for (int v : e[1]) {
      dfs2(v, 1);
    }
  }
} A, B;

set <u64> S;

int main() {
  pw[0] = 1;
  for (int i = 1; i < 100005; i++) {
    pw[i] = base * pw[i - 1];
  }
  scanf("%d", &n);
  A.N = n, B.N = n + 1;
  A.build(), B.build();
  for (int i = 1; i <= A.N; i++) {
    S.insert(A.dp2[i]);
  }
  for (int i = 1; i <= B.N; i++) {
    if (B.e[i].size() > 1) continue;
    u64 x = B.dp2[i] - B.N;
    if (S.find(x) != S.end()) {
      printf("%d", i); return 0;
    }
  }
  assert(0);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Juanzhang/p/11332632.html