zoukankan      html  css  js  c++  java
  • Luogu P4705 玩游戏

    题目描述
    Alice 和 Bob 又在玩游戏。

    对于一次游戏,首先 Alice 获得一个长度为 ​ 的序列 ​,Bob 获得一个长度为 ​ 的序列 bb。之后他们各从自己的序列里随机取出一个数,分别设为 ​,定义这次游戏的 ​次价值为​。

    由于他们发现这个游戏实在是太无聊了,所以想让你帮忙计算对于 ​一次游戏​ 次价值的期望是多少。

    由于答案可能很大,只需要求出模 ​下的结果即可。

    输入输出格式
    输入格式:

    第一行两个整数 ​,分别表示 Alice 和 Bob 序列的长度。

    接下来一行 ​ 个数,第 ​ 个数为 ​,表示 Alice 的序列。

    接下来一行 ​ 个数,第 ​ 个数为 ​,表示 Bob 的序列。

    接下来一行一个整数 ​,意义如上所述。

    输出格式:

    共 ​ 行,第 ​ 行表示一次游戏 ​ 次价值的期望。

    输入输出样例
    输入样例#1:

    复制

    1 1
    1
    2
    3
    输出样例#1:

    复制

    3
    9
    27
    输入样例#2:

    复制

    2 8
    764074134 743107904
    663532060 183287581 749169979 7678045 393887277 27071620 13482818 125504606
    6
    输出样例#2:

    复制

    774481679
    588343913
    758339354
    233707576
    36464684
    461784746
    神仙题啊!!

    前置知识:

    生成函数。

    Taylor展开。

    NTT。

    多项式求​。

    ​次价值得期望就是​。

    用二项式定理将分子展开:

    考虑将​展开:

    我们设:

    答案就是​

    问题的关键就在于预处理出​。

    预处理出这个多项式的方法是一个经典套路(根本不会)。












    通过​的系数可以预处理出A和B。

    解释一下,最后一步变换:

    根据Taylor展开:

    ​求导如下:

    复合函数求导遵循链式法则:

    于是:

    我们再​处展开,也就是令​

    得到:

    于是我们求出​然后就可以用第​项得系数来算出​和​。

    代码(算​的逆元的时候乘爆了,调了一晚上):

    include<bits/stdc++.h>

    define ll long long

    define N 300005

    define mod 998244353

    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch'-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return xf;}

    ll n,m;
    int t;
    ll a[N],b[N];
    ll fac[N<<2],inv[N<<2];

    ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t
    t%mod)
    if(x&1) ans=ans*t%mod;
    return ans;
    }
    ll Mod(ll a) {
    if(a<0) return a+mod;
    if(a>=mod) return a-mod;
    return a;
    }
    int rev[N<<2];
    void NTT(ll *a,int d,int flag) {
    static const ll G=3;
    int n=1<<d;
    for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int s=1;s<=d;s++) {
    int len=1<<s,mid=len>>1;
    ll w=flag
    1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
    for(int i=0;i<n;i+=len) {
    ll t=1;
    for(int j=0;j<mid;j++,t=tw%mod) {
    ll u=a[i+j],v=a[i+j+mid]
    t%mod;
    a[i+j]=Mod(u+v);
    a[i+j+mid]=Mod(u-v+mod);
    }
    }
    }
    if(flag-1) {
    ll inv=ksm(n,mod-2);
    for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
    }
    }

    struct poly {
    ll g[N<<2],f[N<<2];
    ll a[N<<2],ans[N<<2];
    void solve(int l,int r) {
    if(l
    r) return ;
    int len=r-l+1,mid=l+r>>1;
    solve(l,mid),solve(mid+1,r);
    int d=ceil(log2(len+1));
    for(int i=0;i<(1<<d);i++) g[i]=f[i]=0;
    g[0]=f[0]=1;
    for(int i=l;i<=mid;i++) f[i-l+1]=a[i];
    for(int i=mid+1;i<=r;i++) g[i-mid]=a[i];
    NTT(f,d,1),NTT(g,d,1);
    for(int i=0;i<(1<<d);i++) f[i]=f[i]g[i]%mod;
    NTT(f,d,-1);
    for(int i=l;i<=r;i++) a[i]=f[i-l+1];
    }
    ll inv[N<<2],A[N<<2];
    void Inverse(ll a,int d,ll f) {
    f[0]=ksm(a[0],mod-2);
    for(int s=1;s<=d;s++) {
    int len=1<<s,Len=len<<1;
    for(int i=0;i<len;i++) A[i]=a[i],A[i+len]=0;
    NTT(A,s+1,1),NTT(f,s+1,1);
    for(int i=0;i<Len;i++) f[i]=(2
    f[i]-f[i]
    f[i]%mod
    A[i]%mod+mod)%mod;
    NTT(f,s+1,-1);
    for(int i=len;i<Len;i++) f[i]=0;
    }
    }
    void DER(ll a,int n) {
    for(int i=0;i<n;i++) a[i]=a[i+1]
    (i+1)%mod;
    a[n]=a[n-1]=0;
    }
    void INT(ll a,int n) {
    for(int i=n-1;i>=0;i--) a[i+1]=a[i]
    ksm(i+1,mod-2)%mod;
    a[0]=0;
    }
    void Ln(ll *a,int d,ll f) {
    Inverse(a,d,inv);
    DER(a,1<<d);
    NTT(inv,d+1,1),NTT(a,d+1,1);
    for(int i=0;i<(1<<d+1);i++) f[i]=inv[i]
    a[i]%mod;
    NTT(f,d+1,-1);
    INT(f,1<<d);
    }
    void mian(int n,int lim,ll *orig,ll f) {
    for(int i=1;i<=n;i++) a[i]=orig[i];
    solve(1,n);
    int d=ceil(log2(lim));
    a[0]=1;
    Ln(a,d,ans);
    for(int i=1;i<=(1<<d);i++) {
    f[i]=ans[i];
    f[i]=f[i]
    ::inv[i-1]%mod;
    if(!(i&1)) f[i]=(mod-f[i])%mod;
    }
    }
    }pre[2];
    ll A[N<<2],B[N<<2];
    int main() {
    n=Get(),m=Get();
    for(int i=1;i<=n;i++) a[i]=Get();
    for(int i=1;i<=m;i++) b[i]=Get();
    t=Get();

    fac[0]=1;
    for(int i=1;i<=t;i++) fac[i]=fac[i-1]*i%mod;
    
    inv[t]=ksm(fac[t],mod-2);
    for(int i=t-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
    
    int lim=max(n,m)+t;
    pre[0].mian(n,lim,a,A);
    pre[1].mian(m,lim,b,B);
    
    int d=ceil(log2(t*2));
    A[0]=n,B[0]=m;
    for(int i=t+1;i<(1<<d);i++) A[i]=B[i]=0;
    
    
    NTT(A,d,1),NTT(B,d,1);
    


    for(int i=0;i<(1<<d);i++) A[i]=A[i]*B[i]%mod;
    NTT(A,d,-1);

    ll invnm=ksm(1ll*n*m%mod,mod-2);
    
    for(int i=1;i<=t;i++) {
        cout<<fac[i]*A[i]%mod*invnm%mod<<"
    ";
    }
    
    return 0;
    

    }

  • 相关阅读:
    DataGridView在vb.net中的操作技巧
    0、(空字串)、Null、Empty、与Nothing的区别
    System.Timers.Timer与System.Windows.Forms.Timer 区别
    C#判断常见类型格式是否正确的类
    C#对系统注册表操作的类
    Socket基础知识分享
    怎样才能充分利用SQL索引
    通过建立Socket连接来快速判断数据库连接是否正确
    C#中各种数据类型转换的方法的类
    VB.Net C#代码转换工具
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10042573.html
Copyright © 2011-2022 走看看