acm-(多项式、分治FFT)2020中国大学生程序设计竞赛(CCPC) - 网络选拔赛 Residual Polynomial

题面
传送门
f i , j f_{i,j} fi,j表示 f i ( x ) f_i(x) fi(x)的第 j , ( 0 ≤ j ≤ n ) j,(0\le j\le n) j,(0jn)项的系数,我们容易发现如下的转移关系:
f i , j ⋅ j b i + 1 → f i + 1 , j − 1 f i , j ⋅ c i + 1 → f i + 1 , j f_{i,j}\cdot jb_{i+1}\rightarrow f_{i+1,j-1}\\f_{i,j}\cdot c_{i+1}\rightarrow f_{i+1,j} fi,jjbi+1fi+1,j1fi,jci+1fi+1,j
箭头的意思是加过去的意思。
通过转移关系可以形成一条条路径,将一条路径上的边权的乘积叫做路径权值。
考虑 f 1 , i f_{1,i} f1,i f n , j f_{n,j} fn,j的贡献,容易发现贡献就是从 f 1 , i f_{1,i} f1,i f n , j f_{n,j} fn,j的所有路径的权值之和,注意到这条路径上我们恰好需要选择 i − j i-j ij b b b项和 n − 1 − ( i − j ) n-1-(i-j) n1(ij) c c c项,因此可以考虑 d p dp dp,即设 d p l , r [ k ] dp_{l,r}[k] dpl,r[k]表示 [ l , r ] [l,r] [l,r]区间内选择 k k k b b b项对应的乘之和,于是有 d p l , r [ k ] = ∑ i + j = k d p l , m i d [ i ] ⋅ d p m i d + 1 , r [ j ] dp_{l,r}[k]=\sum_{i+j=k}dp_{l,mid}[i]\cdot dp_{mid+1,r}[j] dpl,r[k]=i+j=kdpl,mid[i]dpmid+1,r[j],这是一个典型的卷积式,在递归回溯的时候用FFT合并即可,总复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

求出了 d p [ k ] dp[k] dp[k]数组后,那么 a n s [ j ] = ∑ i − j = k f 1 , i ⋅ d p [ k ] ⋅ i ! j ! ans[j]=\sum_{i-j=k}f_{1,i}\cdot dp[k]\cdot \frac{i!}{j!} ans[j]=ij=kf1,idp[k]j!i!,这也可以写成一个卷积式的形式,然后再做一次FFT就求出来 a n s ans ans数组了。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 400005;
const int mod = 998244353,g=3,gi=332748118;
typedef long long ll;
int qpow(int a,int b,int mod){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        b>>=1;
        a=1ll*a*a%mod;
    }
    return ans;
}
int a[maxn],b[maxn],c[maxn],pos[maxn],fav[maxn],fac[maxn],dp[maxn],aux1[maxn],aux2[maxn],h[maxn];
int as[maxn];
void ntt(int *a,int n,int fg){
    if(n==1)return;
    for(int i=0;i<n;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
    for(register int len=2,mid=1;len<=n;len*=2,mid*=2){
        int wn=qpow(fg==1?g:gi,(mod-1)/len,mod);
        for(register int i=0;i<n;i+=len){
            int w=1;
            for(int j=0;j<mid;++j){
                ll c=a[i+j],d=1ll*w*a[i+j+mid]%mod;
                a[i+j]=(c+d)%mod;
                a[i+j+mid]=(c-d+mod)%mod;
                w=1ll*w*wn%mod;
            }
        }
    }
    int invn=qpow(n,mod-2,mod);
    if(fg==-1)for(int i=0;i<n;++i)a[i]=1ll*a[i]*invn%mod;
}

void pre(int n)
{
    fac[0]=1;
    for(int i =1;i<=n;++i){
        fac[i]=1ll*fac[i-1]*i%mod;
    }
    fav[n]=qpow(fac[n],mod-2,mod);
    for(int i =n-1;i>=0;i--){
        fav[i]=1ll*fav[i+1]*(i+1)%mod;
    }
}

int init(int n,int m)
{
    int len=1,bt=0;
    while(len<n+m-1)len*=2,bt++;
    for(int i=0;i<len;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(bt-1));
    return len;
}
/*
vector<int> ntt1(const vector<int>&a,const vector<int>&b)
{
    int n=a.size(),m=b.size();
    vector<int>ans(n+m-1);
    if(n<=50 && m<=50){
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                ans[i+j]=(ans[i+j]+1ll*a[i]*b[j]%mod)%mod;
            }
        }
        return ans;
    }
    for(int i=0;i<n;++i)aux1[i]=a[i];
    for(int i=0;i<m;++i)aux2[i]=b[i];
    int len=init(n,m);
    ntt(aux1,len,1);
    ntt(aux2,len,1);
    for(int i=0;i<len;++i)aux1[i]=1ll*a[i]*b[i]%mod;
    ntt(aux1,len,-1);
    for(int i=0;i<ans.size();++i)ans[i]=aux1[i];
    for(int i=0;i<len;++i)aux1[i]=aux2[i]=0;
    return ans;
}*/
void work(int l,int r)
{
    if(l==r){
        as[2*l]=c[l];
        as[2*l+1]=b[l];
        return;
    }
    int mid=l+r>>1;
    work(l,mid),work(mid+1,r);
    int n=0,m=0;
    for(int i=2*l,cnt=mid-l+2;cnt;i++,cnt--)aux1[n++]=as[i];
    for(int i=2*(mid+1),cnt=r-mid+1;cnt;i++,cnt--)aux2[m++]=as[i];
    int len=init(n,m);
    ntt(aux1,len,1);
    ntt(aux2,len,1);
    for(int i=0;i<len;++i)aux1[i]=1ll*aux1[i]*aux2[i]%mod;
    ntt(aux1,len,-1);
    for(int i=0;i<=r-l+1;++i)as[2*l+i]=aux1[i];
    for(int i =0;i<len;++i)aux1[i]=aux2[i]=0;
    return;
}
int main(){
    pre(maxn-1);
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i= 0;i<=n;++i)scanf("%d",&a[i]);
        for(int i =0;i<n-1;++i)scanf("%d",&b[i]);
        for(int i=0;i<n-1;++i)scanf("%d",&c[i]);
        work(0,n-2);
        int len=init(n+1,n+1);
        for(int i=0;i<=n-1;++i)dp[i]=as[i];dp[n]=0;
        for(int i =0;i<=n;++i)h[i]=1ll*a[n-i]*fac[n-i]%mod;
        ntt(dp,len,1);
        ntt(h,len,1);
        for(int i =0;i<len;++i)dp[i]=1ll*dp[i]*h[i]%mod;
        ntt(dp,len,-1);
        for(int i =n;i>=0;--i){
            printf("%lld",1ll*dp[i]*fav[n-i]%mod);
            if(i)putchar(' ');
        }
        for(int i=0;i<len;++i)dp[i]=h[i]=0;
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值