zoukankan      html  css  js  c++  java
  • 题解 P6667/BZOJ4734 【[清华集训2016] 如何优雅地求和】

    简化题面

    已知 (m) 次多项式函数 (f)(0,1dots,m) 处的取值(f(0),f(1),dots,f(m)),给定(n,x),求

    [sumlimits_{k=0}^{n}f(k)inom{n}{k}x^{k}(1-x)^{n-k} ]

    答案对(998244353)取模

    解题思路

    看起来很不好求,我们可以把(f)转成下降幂(f(x)=sum_{i=0}^{m}f_i imes x(i))

    其中我们有(x(i)=prod_{k=0}^{i-1}(x-k)=frac{x!}{(x-k)!})

    那么原柿子可以写成

    [sumlimits_{k=0}^{n}f(k)inom{n}{k}x^{k}(1-x)^{n-k} ]

    [=sum_{i=0}^{m}f_isum_{k=i}^{n}frac{n!}{k!(n-k)!} frac{k!}{(k-i)!}x^k (1-x)^{n-k} ]

    [=sum_{i=0}^{m}f_in(i)x^isum_{k=i}^{n}inom{n-i}{k-i}x^{k-i}(1-x)^{n-k} ]

    然后你看到后面那一坨(k)是从(i)开始的,转变枚举范围后面就变成了

    [sum_{k=0}^{n-i}inom{n-i}{k}x^k(1-k)^{n-k-i} ]

    这不是二项式定理吗= =,然后可以求得后面那一堆柿子恒等于((1-k+k)^{n-i}=1)

    那么我们的柿子就变成了

    [sum_{i=0}^{m}f_in(i)x^i ]

    那么我们考虑怎么求(f_i)

    我们有

    [sum_{n}f(n)frac{x^n}{n!} ]

    [=sum_{i=0}^mf_isum_{n=i}frac{x^n}{(n-i)!} ]

    [=sum_{i=0}^mf_ix^isum_{n=i}frac{1}{(n-i)!} ]

    [=(sum_{i=0}^mf_ix^i)e^x ]

    那么我们的(f_i)就是(sum_{n}f(n)frac{x^n}{n!})(e^{-x})的卷积了

    直接大力(NTT)即可,复杂度(O(nlogn))

    (mathcal{Code})

    // Author: Ame__
    #include<bits/stdc++.h>
    #define _ 0
    #define qwq double
    #define AME__DEBUG
    #define LL long long
    #define bomb exit(0)
    #define LOG(FMT...) fprintf(stderr , FMT)
    #define TOWA(FMT...) fprintf(stdout , FMT)
    using namespace std;
    /*Grievous Lady*/
        
    const int BUF_SIZE = 1 << 12;
        
    char buf[BUF_SIZE] , *buf_s = buf , *buf_t = buf + 1;
        
    #define PTR_NEXT() 
    { 
        buf_s ++; 
        if(buf_s == buf_t) 
        { 
            buf_s = buf; 
            buf_t = buf + fread(buf , 1 , BUF_SIZE , stdin); 
        } 
    }
        
    #define mians(_s_) 
    { 
        while(!isgraph(*buf_s)) PTR_NEXT();
        char register *_ptr_ = (_s_); 
        while(isgraph(*buf_s) || *buf_s == '-') 
        { 
            *(_ptr_ ++) = *buf_s; 
            PTR_NEXT(); 
        } 
        (*_ptr_) = ''; 
    }
        
    template <typename _n_> void mian(_n_ & _x_){
        while(*buf_s != '-' && !isdigit(*buf_s)) PTR_NEXT();
        bool register _nega_ = false; if(*buf_s == '-'){ _nega_ = true; PTR_NEXT(); }
        _x_ = 0; while(isdigit(*buf_s)){ _x_ = _x_ * 10 + *buf_s - '0'; PTR_NEXT(); } if(_nega_) _x_ = -_x_;
    }
        
    #define mod 998244353
    
    const int kato = 4e6 + 10;
    
    template <typename _n_> bool cmax(_n_ &a , const _n_ &b){ return a < b ? a = b , 1 : 0; }
        
    template <typename _n_> bool cmin(_n_ &a , const _n_ &b){ return a > b ? a = b , 1 : 0; }
        
    int n , m , x , qaq , ans , len = 1;
    
    int A[kato] , B[kato] , fac[kato] , inv[kato] , fac_inv[kato];
    
    inline int quick_pow(int a , int b){
        int res = 1;
        for(; b ; b >>= 1 , a = 1LL * a * a % mod){
            if(b & 1){
                res = 1LL * res * a % mod;
            }
        }
        return res;
    }
    
    inline void NTT(int *y , int len , int opt){
        int *rev = new int[len];
        rev[0] = 0;
        for(int i = 1;i < len;i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (len >> 1));
        for(int i = 0;i < len;i ++) if(rev[i] > i) swap(y[rev[i]] , y[i]);
        for(int i = 1;i < len;i <<= 1){
            int G1 = quick_pow(3 , (mod - 1) / (i << 1));
            for(int j = 0;j < len;j += (i << 1)){
                for(int k = 0 , g = 1;k < i;k ++ , g = 1LL * g * G1 % mod){
                    int res = 1LL * y[i + j + k] * g % mod;
                    y[i + j + k] = ((y[j + k] - res) % mod + mod) % mod;
                    y[j + k] = (y[j + k] + res) % mod;
                }
            }
        }
        if(opt == -1){
            reverse(y + 1 , y + len);
            for(int i = 0 , inv = quick_pow(len , mod - 2);i < len;i ++) y[i] = 1LL * y[i] * inv % mod;
        }
        delete []rev; rev = 0x0;
    }
    
    #define init() 
    { 
        fac[0] = fac[1] = inv[0] = inv[1] = fac_inv[0] = fac_inv[1] = 1; 
        for(int i = 2;i <= m;i ++) 
        { 
            fac[i] = 1LL * fac[i - 1] * i % mod; 
            inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod; 
            fac_inv[i] = 1LL * fac_inv[i - 1] * inv[i] % mod; 
        } 
    }
    
    inline int Ame_(){
    #ifdef AME__
        freopen(".in" , "r" , stdin); freopen(".out" , "w" , stdout); int nol_cl = clock();
    #endif
        mian(n) , mian(m) , mian(x); m ++; init();
        for(int i = 0;i < m;i ++){ 
            mian(qaq);
            A[i] = 1LL * qaq * fac_inv[i] % mod;
            B[i] = (i & 1) ? -fac_inv[i] : fac_inv[i];
        }
        for(len = 1 ; len <= 2 * m ; len <<= 1);
        NTT(A , len , 1); NTT(B , len , 1);
        for(int i = 0;i < len;i ++) A[i] = 1LL * A[i] * B[i] % mod;
        NTT(A , len , -1);
        int res = 1;
        for(int i = 0;i < m;i ++){
            ans = (ans + 1LL * A[i] * res % mod) % mod;
            res = ((1LL * res * x % mod * (n - i) % mod) + mod) % mod;
        }
        TOWA("%d
    " , ans);
    #ifdef AME__TIME
        LOG("Time: %dms
    ", int((clock() - nol_cl) / (qwq)CLOCKS_PER_SEC * 1000));
    #endif
        return ~~(0^_^0); /*さようならプログラム*/
    }
        
    int Ame__ = Ame_();
        
    int main(){;}
    
    呐,这份感情,什么时候可以传达呢
  • 相关阅读:
    关于一个单机游戏发行的一些见解
    怎么租借电话号码
    NUMA架构的优缺点
    Windows 安装PostgreSQL
    Oracle查询当前用户下的所有表及sqlplus 设置 列宽
    Xshell调整终端显示的最大行数(缓冲区)
    PostgreSQL 索引膨胀
    Linux 清空缓存
    load多个数据文件的yaml
    Linux LVM逻辑卷配置过程详解(创建,增加,减少,删除,卸载)
  • 原文地址:https://www.cnblogs.com/Ame-sora/p/14297004.html
Copyright © 2011-2022 走看看