zoukankan      html  css  js  c++  java
  • UOJ269【清华集训2016】如何优雅地求和【数论,多项式】

    题目描述:求

    $$sum_{k=0}^nf(k)inom{n}{k}x^k(1-x)^{n-k}$$

    输入$n$,$f(x)$的次数上界$m$,$x$,$f(0,1,ldots,m)$,对$998244353$取模。

    数据范围:$1leq nleq 10^9,1leq mleq 2*10^4,0leq a_i,x<998244353$


    现在来讲正解:首先我们把$f(x)$用下降幂来表示,(看到这里的快点关掉,然后自己推一推)

    $$egin{align*}Ans&=sum_{k=0}^nsum_{i=0}^ma_i*frac{k!}{(k-i)!}frac{n!}{k!(n-k)!}x^k(1-x)^{n-k} \&=sum_{k=0}^nsum_{i=0}^ma_i*frac{n!}{(k-i)!(n-k)!}x^k(1-x)^{n-k} \&=sum_{i=0}^ma_in!sum_{k=i}^nfrac{x^k}{(k-i)!}*frac{(1-x)^{n-k}}{(n-k)!} \&=sum_{i=0}^ma_ix^in!(e^x*e^{1-x}[x^{n-i}]) \&=sum_{i=0}^ma_ix^ifrac{n!}{(n-i)!}end{align*}$$

    然后就是考虑如何点值转下降幂

    $$egin{align*}f(i)&=sum_{j=0}^ma_jfrac{i!}{(i-j)!} \frac{f(i)}{i!}&=sum_{j=0}^ma_j*frac{1}{(i-j)!}end{align*}$$

    所以$f(i)$的EGF等于$a$的OGF乘上$e^x$,所以$a$的OGF等于$f(i)$的EGF乘上$e^{-x}$,使用NTT优化计算,时间复杂度$O(mlog m)$

     1 #include<bits/stdc++.h>
     2 #define Rint register int
     3 using namespace std;
     4 typedef long long LL;
     5 const int N = 1 << 16, mod = 998244353, G = 3, Gi = 332748118;
     6 int n, m, x, A[N], B[N], fac[N], invfac[N], ans;
     7 inline int kasumi(int a, int b){
     8     int res = 1;
     9     while(b){
    10         if(b & 1) res = (LL) res * a % mod;
    11         a = (LL) a * a % mod;
    12         b >>= 1;
    13     }
    14     return res;
    15 }
    16 inline void init(){
    17     fac[0] = 1;
    18     for(Rint i = 1;i <= m;i ++) fac[i] = (LL) i * fac[i - 1] % mod;
    19     invfac[m] = kasumi(fac[m], mod - 2);
    20     for(Rint i = m;i;i --) invfac[i - 1] = (LL) i * invfac[i] % mod;
    21 }
    22 int rev[N];
    23 inline int calrev(int len){
    24     int limit = 1, L = -1;
    25     while(limit <= len){limit <<= 1; L ++;}
    26     for(Rint i = 0;i < limit;i ++)
    27         rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L);
    28     return limit;
    29 }
    30 inline void NTT(int *A, int limit, int type){
    31     for(Rint i = 0;i < limit;i ++)
    32         if(i < rev[i]) swap(A[i], A[rev[i]]);
    33     for(Rint mid = 1;mid < limit;mid <<= 1){
    34         int Wn = kasumi(type == 1 ? G : Gi, (mod - 1) / (mid << 1));
    35         for(Rint j = 0;j < limit;j += (mid << 1)){
    36             int w = 1;
    37             for(Rint k = 0;k < mid;k ++, w = (LL) w * Wn % mod){
    38                 int x = A[j + k], y = (LL) w * A[j + k + mid] % mod;
    39                 A[j + k] = (x + y) % mod;
    40                 A[j + k + mid] = (x - y + mod) % mod;
    41             }
    42         }
    43     }
    44     if(type == -1){
    45         int inv = kasumi(limit, mod - 2);
    46         for(Rint i = 0;i < limit;i ++)
    47             A[i] = (LL) A[i] * inv % mod;
    48     }
    49 }
    50 int main(){
    51     scanf("%d%d%d", &n, &m, &x);
    52     init();
    53     for(Rint i = 0;i <= m;i ++){
    54         scanf("%d", A + i);
    55         A[i] = (LL) A[i] * invfac[i] % mod;
    56         B[i] = invfac[i];
    57         if(i & 1) B[i] = mod - B[i];
    58     }
    59     int limit = calrev(m << 1);
    60     NTT(A, limit, 1); NTT(B, limit, 1);
    61     for(Rint i = 0;i < limit;i ++) A[i] = (LL) A[i] * B[i] % mod;
    62     NTT(A, limit, -1);
    63     int w = 1;
    64     for(Rint i = 0;i <= m;i ++){
    65         ans = (ans + (LL) w * A[i] % mod) % mod;
    66         w = (LL) w * (mod + n - i) % mod * x % mod;
    67     }
    68     printf("%d", ans);
    69 }
    UOJ269

     启发:推柿子遇到多项式时应考虑多项式的各种形式,如点值,普通/上升/下降幂等。

  • 相关阅读:
    The Fifth Week Lucklyzpp
    The Fourth Week Lucklyzpp
    The Third Week Lucklyzpp
    The Second Week lucklyzpp
    快10年没怎么过来了,一切如常
    男女诗篇
    ubuntu安装mysql
    ubuntu配置tomcat和jdk
    ubuntu常用操作命令以及它的通道模式简解
    Ubuntu操作异常汇总
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/11166909.html
Copyright © 2011-2022 走看看