UOJ #390. 【UNR #3】百鴿籠
阿新 • • 發佈:2018-12-22
UOJ #390. 【UNR #3】百鴿籠
看這道題之前先看一道相似的題目 【PKUWC2018】獵人殺。
考慮類似的容斥:
我們不妨設處理\(1\)的概率。
我們另集合\(T\)中的所有鴿籠都在\(1\)變空之前不為空的,其它的鴿籠隨便。要做到這一點,我們只需要令每個\(T\)集合中的鴿籠容量\(--\)就行了。然後我們用揹包背出所有序列的方案數(不包括\(1\)),然後在將\(1\)插入序列中。插入時,將\(w_i-1\)個隨便插入,然後再將一個放在序列末尾。
具體實現時,我們可以列舉"\(1\)",然後對其它的鴿籠進行揹包。但是複雜度會達到\(O(n^6)\)。於是我們先對所有鴿籠進行揹包,計算"\(1\)
程式碼:
#include<bits/stdc++.h> #define ll long long #define N 35 #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 x*f;} int n,w[N]; 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 fac[1005],inv[1005]; ll C(int n,int m) { if(n<m) return 0; return fac[n]*inv[m]%mod*inv[n-m]%mod; } ll f[N][N*N]; ll g[N][N*N]; int sum; void solve(int now) { memcpy(f,g,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=0;j<=sum;j++) { for(int q=0;q<w[now]&&q<=j;q++) { f[i][j]=(f[i][j]-f[i-1][j-q]*C(j,q)%mod+mod)%mod; } } } ll ans=0,flag=1; for(int i=0;i<n;i++,flag*=-1) { ll invi=ksm(i+1,mod-2),t=ksm(invi,w[now]); for(int j=0;j<=sum;j++,t=t*invi%mod) { if(!f[i][j]) continue ; (ans+=flag*C(j+w[now]-1,w[now]-1)*f[i][j]%mod*t%mod)%=mod; } } cout<<(ans+mod)%mod<<" "; } int main() { fac[0]=1; for(int i=1;i<=900;i++) fac[i]=fac[i-1]*i%mod; inv[900]=ksm(fac[900],mod-2); for(int i=899;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; n=Get(); for(int i=1;i<=n;i++) w[i]=Get(); g[0][0]=1; for(int i=1;i<=n;i++) { sum+=w[i]-1; for(int j=i;j>=1;j--) { for(int k=sum;k>=0;k--) { for(int q=0;q<w[i]&&q<=k;q++) { (g[j][k]+=g[j-1][k-q]*C(k,q))%=mod; } } } } for(int i=1;i<=n;i++) solve(i); return 0; }