1. 程式人生 > >Luogu P4705 玩遊戲

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=anst%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]=(2f[i]-f[i]f[i]%modA[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<<"\n";
}

return 0;

}