[2018HN省隊集訓D8T3] 水果拼盤
阿新 • • 發佈:2019-03-10
char 分享 ans .cn 普通 參考 自帶 value tps
就是一個普通多項式, 那麽我們可以直接算出這個多項式的每一項系數把它作為容斥系數.
[2018HN省隊集訓D8T3] 水果拼盤
題意
給定 \(n\) 個集合, 每個集合包含 \([1,m]\) 中的一些整數, 在這些集合中隨機選取 \(k\) 個集合, 求這 \(k\) 個集合的並集的權值的期望.
一個集合的權值定義為, 對於所有 \([1,m]\) 的整數, 若集合中含有 \(i\) 則產生 \(a_i\) 的貢獻, 否則產生 \(b_i\) 的貢獻.
\(n\le 1\times 10^5, m\le 18,k\le 25\)
題解
好像只有我一個寫了一些玄學FWT操作...別人都是組合數直接碾的qaq
顯然我們可以通過求所有最終集合的生成概率來計算出最終期望. 而這個概率顯然就是個或卷積的形式.
於是我們可以FWT一發.
接著我們發現直接FWT卷 \(k\) 次可能會有重復的方案. (就像[BZOJ 3771] Triple那題). 於是我們需要考慮容斥.
然而這次是廣義容斥, 普通二項式反演出來是假的.
stdcall&棟棟說過廣義容斥瞎換一波系數就過了, 於是思考一些奇怪的東西來湊容斥系數.
FWT卷 \(k\) 次後得到的方案數是 \(n^k\), 而我們實際上需要的不重復的方案數應該是 \(n^{\underline k}\) (卷積出來的方案有序, 要自帶一個全排列), 那麽我們需要用一些玄學系數用 \(n^k\) 湊出 \(n^{\underline k}\).
註意到其實 \(n^{\underline k}\)
實際上就是帶符號第一類斯特林數. 用這個系數容斥一下就好了.
FWT一次後的點值可以重復使用, 所以總時間復雜度是 \(O(\sum|S|+(k+m)2^m)\).
參考代碼
#include <bits/stdc++.h> const int MAXK=27; const int MAXL=1e6+10; const int MOD=998244353; int n; int m; int k; int a[MAXL]; int pw[MAXK]; int ans[MAXL]; int cof[MAXK]; int c[MAXK][2]; void FWT(int*,int); void IFWT(int*,int); inline int ReadInt(); inline int Pow(int,int,int); int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=0;i<m;i++) scanf("%d",c[i]+1); for(int i=0;i<m;i++) scanf("%d",c[i]); cof[0]=1; for(int i=0;i<k;i++){ for(int j=i+1;j>0;j--) cof[j]=(cof[j-1]-1ll*cof[j]*i%MOD+MOD)%MOD; cof[0]=(MOD-1ll*cof[0]*i%MOD)%MOD; } for(int i=0;i<n;i++){ int cnt=ReadInt(),s=0; while(cnt--) s|=(1<<(ReadInt()-1)); ++a[s]; } int maxs=1<<m; FWT(a,maxs); pw[0]=1; for(int s=0;s<maxs;s++){ for(int i=1;i<=k;i++) pw[i]=1ll*pw[i-1]*a[s]%MOD; for(int i=0;i<k;i++) ans[s]=(ans[s]+1ll*pw[k-i]*cof[k-i])%MOD; } IFWT(ans,maxs); int cnt=0; int sum=0; for(int s=0;s<maxs;s++){ (cnt+=ans[s])%=MOD; for(int i=0;i<m;i++) sum=(sum+1ll*c[i][(s>>i)&1]*ans[s])%MOD; } printf("%lld\n",1ll*sum*Pow(cnt,MOD-2,MOD)%MOD); return 0; } inline void FWT(int* a,int len){ for(int i=1;i<len;i<<=1) for(int j=0;j<len;j+=(i<<1)) for(int k=0;k<i;k++){ a[j+k+i]+=a[j+k]; a[j+k+i]=(a[j+k+i]>=MOD?a[j+k+i]-MOD:a[j+k+i]); } } inline void IFWT(int* a,int len){ for(int i=1;i<len;i<<=1) for(int j=0;j<len;j+=(i<<1)) for(int k=0;k<i;k++){ a[j+k+i]-=a[j+k]; a[j+k+i]=(a[j+k+i]<0?a[j+k+i]+MOD:a[j+k+i]); } } inline int ReadInt(){ int x=0; register char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)){ x=x*10+ch-'0'; ch=getchar(); } return x; } inline int Pow(int a,int n,int p){ int ans=1; while(n>0){ if(n&1) ans=1ll*a*ans%p; a=1ll*a*a%p; n>>=1; } return ans; }
[2018HN省隊集訓D8T3] 水果拼盤