Codeforces 954H Path Counting

rizynvu發表於2024-04-21

令輸入的為 \(a'\),同時 \(a'_0 = 1\)
對其做一個字首積 \(a_i = \prod\limits_{i = 0}^i a'_i\),對於 \(i\ge n\),認為 \(a_i = 0\)
那麼 \(a_i\) 就相當於是深度 \(i + 1\) 的點的個數。
同時也可以得到根的深度為 \(l\) 時子樹內深度為 \(r\) 的深度的點數為 \(\dfrac{a_{r - 1}}{a_{l - 1}}\)

考慮固定了距離為 \(d\) 來算。
那麼有兩種情況:

  1. 兩點為祖先關係,那麼考慮到深度 \(\ge d + 1\) 的點都能找到其祖先。
    所以貢獻就為 \(\sum\limits_{i = d}^{n - 1} a_i\)
  2. 兩點不為祖先關係,考慮列舉 \(\text{LCA}\) 的深度 \(i\),那麼就會有 \(a_{i - 1}\) 個點。
    首先這兩個點不能為祖先關係,所以對於 \(\text{LCA}\),這兩個點一定在其兩個不同的兒子的子樹中,這部分的方案數就為 \(\binom{a'_i}{2} = \binom{\frac{a_i}{a_{i - 1}}}{2}\)
    然後考慮列舉其中一個點的深度為 \(i + j(1\le j < d)\),那麼另一個點的深度就為 \(i + d - j\),那麼方案數就為 \(\dfrac{a_{i + j - 1} a_{i + d - j - 1}}{a_i^2}\)
    於是方案數就相當於是 \(\sum\limits_{i = 1}^{n - 1} a_{i - 1}\binom{\frac{a_i}{a_{i - 1}}}{2}\frac{1}{a_i^2}\sum\limits_{1\le j < d} a_{i + j - 1} a_{i + d - j - 1}\)

考慮最佳化第二部分。
能發現主要是 \(\sum\limits_{1\le j < d} a_{i + j - 1} a_{i + d - j - 1}\) 這部分每次都需要 \(\mathcal{O}(n)\) 的複雜度,考慮最佳化這部分。
\(f_{i, d} = \sum\limits_{1\le j < d} a_{i + j - 1} a_{i + d - j - 1}\),能發現這也就是 \(\sum\limits_{x, y\ge i, x + y = 2i + d - 2} a_x a_y\)
初始情況顯然有 \(f_{i, 1} = 0, f_{i, 2} = a_i^2\)
對於 \(d\ge 3\),考慮 \(f_{i + 1, d - 2} = \sum\limits_{x, y\ge i + 1, x + y = 2i + d - 2} a_x a_y\),能發現 \(f_{i, d} - f_{i + 1, d - 2} = a_i a_{i + d - 2} + a_{i + d - 2} a_i = 2a_i a_{i + d - 2}\),所以有 \(f_{i, d} = f_{i + 1, d - 2} + 2a_i a_{i + d - 2}\),便可以 \(\mathcal{O}(1)\) 遞推了。

注意到 \(f_{i, d}\) 的遞推只與 \(f_{i + 1, d + 2}\) 有關,可以把 \(d\) 這一維壓成 \(0 / 1\)

時間複雜度 \(\mathcal{O}(n^2)\)

程式碼
#include<bits/stdc++.h>
using ll = long long;
const ll mod = 1e9 + 7;
inline ll binom2(ll n) {
   return n * (n - 1) / 2 % mod;
}
inline ll qpow(ll a, ll b, ll v = 1) {
   while (b) {
      if (b & 1) (v *= a) %= mod;
      b >>= 1, (a *= a) %= mod;
   }
   return v;
}
const int maxn = 5e3 + 10;
ll a[maxn], inva[maxn];
ll f[maxn][2];
int main() {
   int n;
   scanf("%d", &n);
   int m = 2 * n - 2;
   a[0] = 1;
   for (int i = 1; i < n; i++)
      scanf("%lld", &a[i]);
   for (int i = 1; i < n; i++)
      (a[i] *= a[i - 1]) %= mod;
   for (int i = 0; i < n; i++)
      inva[i] = qpow(a[i], mod - 2);
   for (int d = 1; d <= m; d++) {
      ll ans = 0;
      for (int i = 1; i + d <= n; i++)
         (ans += a[i + d - 1]) %= mod;
      if (d == 1);
      else if (d == 2) {
         for (int i = 1; i < n; i++)
            f[i][d & 1] = a[i] * a[i] % mod;
      } else {
         for (int i = 1; i < n; i++) {
            f[i][d & 1] = f[i + 1][d & 1];
            if (i + d - 1 <= n)
               (f[i][d & 1] += 2ll * a[i] * a[i + d - 2]) %= mod;
         }
      }
      for (int i = 1; i < n; i++)
         (ans += f[i][d & 1] % mod * inva[i] % mod * inva[i] % mod
                 * binom2(a[i] * inva[i - 1] % mod) % mod * a[i - 1] % mod) %= mod;
      printf("%lld ", ans);
   }
   return 0;
}

相關文章