1. 程式人生 > >【ARC102E】Stop. Otherwise...(容斥原理,動態規劃)

【ARC102E】Stop. Otherwise...(容斥原理,動態規劃)

【ARC102E】Stop. Otherwise...(容斥原理,動態規劃)

題面

AtCoder
\(n\)個骰子,每個骰子有\(K\)個面,上面有\(1\)\(K\)。骰子都是一樣的。
現在對於\([2,2k]\)中的每一個數\(x\),要求出滿足不存在任意兩個骰子的點數和為\(x\)的方案數。

題解

顯然這個東西是一個容斥計算的過程。
而兩兩之間的點數和恰好為\(x\)的配對方案數也是有限的。
那麼列舉至少出現了\(k\)不合法的數字配對的情況。
得到了:
\[Ans=\sum_{i=0}^t (-1)^t{t\choose i}{n-2i+k-1\choose k-1}\]
其中\(t\)

表示能夠拼出\(x\)的無序點對數。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 998244353
#define MAX 4040
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int jc[MAX],jv[MAX],inv[MAX],n,k,tot[MAX];
int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int main()
{
    scanf("%d%d",&k,&n);
    jc[0]=jv[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n+k;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<=n+k;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    for(int i=1;i<=n+k;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    for(int i=1;i<=k;++i)tot[i+1]+=1,tot[i+k+1]-=1;
    for(int i=1;i<=k+k;++i)tot[i]+=tot[i-1];
    for(int i=2;i<=k+k;++i)
    {
        int cnt=(tot[i]+1)/2,ans=0;
        for(int j=0,d=1;j<=cnt&&j+j<=n;++j,d=MOD-d)
            add(ans,1ll*d*C(cnt,j)%MOD*C(n-2*j+k-1,k-1)%MOD);
        printf("%d\n",ans);
    }
    return 0;
}

然後聽\(ppl\)說還有一種\(dp\)方法。
因為每一對無序對中,都只能選擇恰好一個,所以設\(f[i][j]\)表示從\(i\)個無序對中恰好選擇了\(j\)個的方案數,每個組裡面至少要選擇一個。
那麼轉移就是\(f[i][j]=2*f[i-1][j-1]+f[i][j-1]\)
轉移的兩部分是這樣子看的,前半部分是選擇一個新組,可以從兩個中任選一個,否則強制選擇上一組,並且只能選擇之前選過的那一個。
那麼得到每個組可以不選東西的方案數\(\displaystyle g[i][j]=\sum_{k=0}^i {i\choose k}f[k][j]\)。即考慮選擇了幾個組,然後計算一下方案數。
這樣子一來詢問的時候只需要詢問的考慮就行了。
\(x\)

為奇數的時候,那麼直接列舉多少個可能構成組,然後剩下的在範圍外,隨意組合。
\(x\)為偶數的時候,特殊考慮是否選擇\(x/2\),選了就只能選一個,然後組合數考慮。
\(g\)的時候用\(NTT\)優化,時間複雜度\(O(n^2log)\)
沒有程式碼,要程式碼的話,戳ppl部落格