1 条题解

  • 0
    @ 2025-8-24 21:56:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar NaCly_Fish
    北海虽赊,扶摇可接。

    搬运于2025-08-24 21:56:24,当前版本为作者最后更新于2019-03-30 13:45:22,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这是一篇搬运的题解。
    原作者:

    https://www.luogu.org/space/show?uid=21423
    /HEOI2016] 求和 线性解法](https://blog.csdn.net/EI_Captain/article/details/87906472)
    orz EI!!!


    先来推式子:

    $$ans=\sum\limits_{i=0}^n\sum\limits_{j=0}^iS(i,j)\times j! \times 2^j $$$$=\sum\limits_{i=0}^n\sum\limits_{j=0}^nS(i,j)\times j! \times 2^j $$$$=\sum\limits_{i=0}^n\sum\limits_{j=0}^n2^j\sum\limits_{k=0}^j(-1)^{j-k}k^i \binom{j}{k} $$$$=\sum\limits_{j=0}^n\sum\limits_{k=0}^j(-1)^{j-k} \binom{j}{k} 2^j[n+1]_k $$$$[n]_q=\sum\limits_{i=0}^{n-1}q^i\space\text{is }\color{#66CCFF}\text{q-analog} $$

    大部分人的推导在这里就结束了,因为这个式子可以卷积。
    但是其实这个式子还可以不卷积进行计算。这个东西的要点就是我们要看清楚形如

    j=inqji(ji)aj\sum\limits_{j=i}^nq^{j-i}\binom{j}{i}a_j

    的形式是什么。
    设数列an\langle a_n \rangle的生成函数是G(z)G(z),那么这个和式其实就是带入了G(z+q)G(z+q)
    反观我们的式子中:

    q=1,G(z)=[n+1]2z=(2z)n+112z1q=-1,G(z)=[n+1]_{2z}=\frac{(2z)^{n+1}-1}{2z-1}

    因此,我们得到的[n+1]k[n+1]_k项的系数就由

    G(z1)=(2z2)n+112z3G(z-1)=\frac{(2z-2)^{n+1}-1}{2z-3}

    分子可以用二项式定理化开,然后再除一个一次式,这是可以递推的。

    至于把所有的

    [n+1]k=kn+11k1[n+1]_k=\frac{k^{n+1}-1}{k-1}

    算出来为什么没有快速幂的Θ(nlogn)\Theta(n\log n),那是因为kn+1k^{n+1}关于kk是完全积性函数,合数的kk是可以被筛出来的。这部分的复杂度就是π(n)Θ(logn)=Θ(n)\pi (n) \cdot\Theta(\log n)=\Theta(n)


    解法部分到这里就结束了。
    下面是EI巨佬的代码,不要抄哦

    #include <cstdio>
    #include <cstring>
    
    #include <functional>
    #include <vector>
    
    #define LOG(FMT...) fprintf(stderr, FMT)
    
    using namespace std;
    
    typedef long long ll;
    
    const int P = 998244353;
    
    void exGcd(int a, int b, int& x, int& y) {
      if (!b) {
        x = 1;
        y = 0;
        return;
      }
      exGcd(b, a % b, y, x);
      y -= a / b * x;
    }
    
    int inv(int a) {
      int x, y;
      exGcd(a, P, x, y);
      if (x < 0)
        x += P;
      return x;
    }
    
    int mpow(int x, int k) {
      int ret = 1;
      while (k) {
        if (k & 1)
          ret = ret * (ll)x % P;
        x = x * (ll)x % P;
        k >>= 1;
      }
      return ret;
    }
    
    struct Simple {
      int n;
      vector<int> fac, ifac, inv;
    
      void build(int n) {
        this->n = n;
        fac.resize(n + 1);
        ifac.resize(n + 1);
        inv.resize(n + 1);
        fac[0] = 1;
        for (int x = 1; x <= n; ++x)
          fac[x] = fac[x - 1] * (ll)x % P;
        inv[1] = 1;
        for (int x = 2; x <= n; ++x)
          inv[x] = -(P / x) * (ll)inv[P % x] % P + P;
        ifac[0] = 1;
        for (int x = 1; x <= n; ++x)
          ifac[x] = ifac[x - 1] * (ll)inv[x] % P;
      }
    
      Simple() {
        build(1);
      }
    
      void check(int k) {
        int nn = n;
        if (k > nn) {
          while (k > nn)
            nn <<= 1;
          build(nn);
        }
      }
    
      int gfac(int k) {
        check(k);
        return fac[k];
      }
    
      int gifac(int k) {
        check(k);
        return ifac[k];
      }
    
      int ginv(int k) {
        check(k);
        return inv[k];
      }
    
      int binom(int n, int m) {
        if (m < 0 || m > n)
          return 0;
        return gfac(n) * (ll)gifac(m) % P * gifac(n - m) % P;
      }
    } simp;
    
    const int N = 100010, PC = 30010;
    
    int n;
    int pc;
    int a[N];
    bool vis[N];
    int p[PC];
    int pw[N];
    
    void sieve() {
      pw[1] = 1;
      for (int x = 2; x <= n; ++x) {
        if (!vis[x]) {
          p[++pc] = x;
          pw[x] = mpow(x, n + 1);
        }
        for (int i = 1; x * p[i] <= n; ++i) {
          vis[x * p[i]] = true;
          pw[x * p[i]] = pw[x] * (ll)pw[p[i]] % P;
          if (x % p[i] == 0)
            break;
        }
      }
    }
    
    int main() {
      scanf("%d", &n);
      simp.check(n + 1);
      for (int i = 0; i <= n; ++i)
        a[i] = ((n + 1 - i) & 1) ? (P - simp.binom(n + 1, i)) : simp.binom(n + 1, i);
      int pw = mpow(2, n + 1);
      for (int i = 0; i <= n; ++i)
        a[i] = a[i] * (ll)pw % P;
      --a[0];
      for (int i = 0; i <= n; ++i)
        a[i] = (P - a[i]) % P;
      int q = inv(3) * 2 % P;
      for (int i = 1; i <= n; ++i)
        a[i] = (a[i - 1] * (ll)q + a[i]) % P;
      int ans = (a[0] + a[1] * (ll)(n + 1)) % P;
      sieve();
      for (int i = 2; i <= n; ++i)
        ans = (ans + simp.ginv(i - 1) * (::pw[i] - 1LL) % P * a[i]) % P;
      if (ans < 0)
        ans += P;
      ans = ans * (ll)inv(3) % P;
      printf("%d\n", ans);
    
      return 0;
    }
    

    ps:如果谁对这篇题解有更加易懂的解释,请在讨论区写出来qwq

    • 1

    信息

    ID
    3047
    时间
    3000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者