uoj 33 树上GCD

http://uoj.ac/problem/33

先说部分分

30分暴力合并儿子的信息求解就好了

40分的话可以考虑对于先求出\(\gcd (d(u,a),d(v,a))\)\(d\)的倍数,然后容斥一发

然后先说一种启发式合并的做法吧(个人认为这个做法比较好写

需要求出\(\gcd (d(u,a),d(v,a))\)\(d\)的倍数

\(f[n][d]\)\(n\)这个点子树距离\(n\)\(kd\)的有多少个

\(d\)\(\sqrt n\)大小关系分类讨论

\(d \leq \sqrt n\)

\(\mathcal O(n)\)做一遍拓扑序\(dp\)

此部分复杂度\(\mathcal O(n \sqrt n)\)

\(d > \sqrt n\)

启发式合并次数不会超过\(\sqrt n\)

由于统计答案的时候状态数不会很多

暴力隔\(d\)跳就好了

复杂度经过处理后得到一个上界\(nk \ln {\dfrac{\sqrt n}{k}}\),\(k\)是块大小

\(k = \sqrt n\)时复杂度是小于\(\mathcal O(n \sqrt n)\)

可以参考一下http://vfleaking.blog.uoj.ac/blog/38中lyx的证明

总复杂度为\(\mathcal O(n \sqrt n)\)

还有一种基于分治的做法

同样需要求出\(\gcd (d(u,a),d(v,a))\)\(d\)的倍数

考虑当前的分治重心为\(t\)

会对所有经过他的点对\((u, v)\)会产生贡献

\((u, v)\)\(t\)子树内的点的时候

\(\mathcal O(n \log n)\)暴力统计即可

\((u, v)\)只有一点在\(t\)子树内

枚举\(t\)到当前分治的根\(root\)上的节点\(a_i\)

求出所有\(a_i\)子树的点但是非\(t\)子树的点到\(a_i\)距离

然后枚举\(d\)

同样对\(d\)\(\sqrt n\)大小关系分类讨论

对于\(t\)子树中\(d\le \sqrt n\)预处理

\(d > \sqrt n\)时暴力求解

一层分治复杂度是\(\mathcal O(n \sqrt n)\)

总的复杂度也是\(\mathcal O(n \sqrt n)\)

学习了一发主定理

对于\(T(n) = aT(\dfrac{n}{b}) + f(n)\)

\(g(n) = n ^{log_b^a}\)

\(f<g\)\(T=g\)

\(f=g\)\(T=g \log n\)

\(f > g\)时且对\(\forall c < 1\)且足够大的\(n\)

\(af(\dfrac{n}{b}) \le cf(n)\) \(T = f\)

#include <bits/stdc++.h>
#define int long long
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\n"
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
  x = 0; char c = getchar(); bool f = 0;
  for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
  if(f) x = -x;
}
const int N = 2e5 + 233;
int n;
int fa[N], pred[N], nowd[N], dep[N], has[N], f[N];
int ans[N], tmp[N];
inline void uni(vector<int> &a, vector<int> &b) {
  int sa = (int) a.size(), sb = (int) b.size();
  for(int i = 1; i <= sb; i ++)
    a[sa - i] += b[sb - i];
  b.clear();
}

main(void) {
  read(n);
  dep[1] = 0;
  for(int i = 2; i <= n; i ++) {
    read(fa[i]); nowd[i] = i;
    dep[i] = dep[fa[i]] + 1;
    tmp[1] ++; tmp[dep[i] + 1] --;
  }
  fo(i, n) tmp[i] += tmp[i - 1];

  int K = min((int) sqrt(n), 167ll);
  int maxdep = *max_element(dep + 1, dep + n + 1);
  
  // calculate for d \leq \sqrt n

  fo(d, K) {
    fo(i, n) has[i] = f[i] = 0;
    fo(i, n) {
      pred[i] = nowd[i];
      nowd[i] = fa[nowd[i]];
    }
    for(int i = n; i > 1; i --) {
      f[i] ++;
      has[pred[i]] += f[i];
      if(has[i] && fa[i]) {
        ans[d] += has[i] * f[fa[i]];
        f[fa[i]] += has[i];
      }
    }
  }
  
  // calculate for d > \sqrt n
  vector<int> f[N];

  for(int i = n; i >= 2; i --) {
    f[i].push_back(1);
    if((int) f[i].size() > (int) f[fa[i]].size())
      swap(f[i], f[fa[i]]);
    
    int in = (int) f[i].size();
    int fan = (int) f[fa[i]].size();

    if(in > K && fan > K) {
      int up = min(maxdep, min(in, fan));
      for(int d = K + 1; d <= up; d ++) {
        int cnti = 0, cntfa = 0;
        for(int k = d; k <= in; k += d)
          cnti += f[i][in - k];
        for(int k = d; k <= fan; k += d)
          cntfa += f[fa[i]][fan - k];
        ans[d] += 1ll * cnti * cntfa;
      }
    }

    uni(f[fa[i]], f[i]);
  }

  for(int i = n; i >= 1; i --)
    for(int j = i + i; j <= n; j += i)
      ans[i] -= ans[j];
  
  for(int i = 1; i <= n - 1; i ++)
    cout << ans[i] + tmp[i] << "\n";
}

猜你喜欢

转载自www.cnblogs.com/foreverpiano/p/9230789.html
33