【ARC102E】Stop. Otherwise...

题目

题意:给定\(n\)没有区别\(K\)面骰子,对于\(i\in [2,2K]\)求出有多少种骰子序列使得任意两个骰子的点数和不为\(i\)

考虑对于一种点数限制\(i\),如果使用了点数为\(j\)的骰子,那么点数为\(i-j\)的骰子就不能使用了

于是对于一种限制\(i\),我们可以把\(1\)\(K\)的点数\(j\)分一下类

  • \(j\neq i-j,i-j\in [1,K]\),即\(j\)\(i-j\)是两种不同的合法点数,则这两种点数只能出现一种

  • \(i-j\notin [1,K]\),即\(j\)这种点数的出现没有限制

  • \(i-j=j\),即\(i=2j\),这个时候点数\(j\)只能出现一次

我们发现对于不同的\(i\),都能分成这三类,且第三类会出现当且仅当\(i\)是偶数时

于是我们可以对前两种情况进行讨论

我们记第一种情况点数对有\(cnt1\),第二种情况点数有\(cnt2\)

假设我们有两个数组,一个\(dp_{i,j}\)表示用不超过\(i\)对互斥点数对凑出\(j\)个数有多少情况,\(f_{i,j}\)表示用\(i\)种点数凑成\(j\)个数有多少情况,我们的答案就是

\[\sum_{j=0}^ndp_{cnt1,j}\times f_{cnt2,n-j}\]

\(i\)为偶数的时候,还需要加上\(\sum_{j=0}^{n-1}dp_{cnt1,j}\times f_{cnt2,n-1-j}\),即空出一个数给$
\frac{i}{2}$的请况

考虑\(f,dp\)如何求出

\(f\)比较好求,显然有\(f_{i,j}=\sum_{k=0}^jf_{i-1,k}\),即对于第\(i\)种点数枚举一下它选了多少个

\(dp\)表示的含义是互斥点数对,于是对于一对互斥点数对,可以是出现两种点数之一,于是\(dp_{i,j}=dp_{i-1,j}+2\times\sum_{k=0}^{k-1}dp_{i-1,k}\),即枚举这一对点对选了多少个,由于两种都可以填,所以要乘\(2\)

代码

#include<bits/stdc++.h>
#define re register
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=998244353;
const int maxn=2005;
int n,m,a[maxn];
int dp[maxn][maxn],f[maxn][maxn];
inline int qm(int x) {return x>=mod?x-mod:x;}
inline int dqm(int x) {return x<0?x+mod:x;}
inline int calc(int n,int a,int b) {
    int now=0;
    for(re int j=0;j<=n;j++)
        now=qm(now+1ll*dp[a][j]*f[b][n-j]%mod);
    return now;
}
int main() {
    scanf("%d%d",&m,&n);
    a[0]=1;dp[0][0]=1;for(re int i=1;i<=m;i++) dp[i][0]=1;
    for(re int i=1;i<=m;i++) {
        for(re int j=1;j<=n;j++)
            a[j]=qm(a[j-1]+dp[i-1][j]);
        for(re int j=1;j<=n;j++)
            dp[i][j]=qm(2ll*a[j-1]%mod+dp[i-1][j]);
    }
    for(re int i=0;i<=m;i++) f[i][0]=1;
    for(re int i=1;i<=m;i++)
        for(re int j=1;j<=n;j++)
            f[i][j]=qm(f[i][j-1]+f[i-1][j]);
    int cnt1=0,cnt2=0,ans=0;
    for(re int i=2;i<=2*m;++i) {
        cnt1=cnt2=0;
        for(re int j=1;j<=m;j++) {
            if(i-j==j) continue;
            if(i-j>=1&&i-j<=m) {
                if(j<i-j) ++cnt1;
            }
            else ++cnt2;
        }
        ans=calc(n,cnt1,cnt2);
        if(!(i&1)) ans=qm(ans+calc(n-1,cnt1,cnt2));
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/asuldb/p/11620967.html