[数学记录]AT4362 [ARC102C] Stop. Otherwise...

command_block

2021-05-04 08:10:45

Personal

**题意** :有 $n$ 个骰子,每个骰子有 $k$ 个面,上面有 $1\sim k$。 现在对于 $[2,2k]$ 中的每一个数 $x$,求出任意投这个 $n$ 个骰子使得不存在任意两个骰子的点数和为 $x$ 的方案数。 骰子之间本质相同(无标号)。 答案对 $998244353$ 取模。 $n,k\leq 2000$ ,时限$\texttt{2s}$。 ------------ 只考虑 $x\leq k+1$ 时的情况,另一侧的方案数是对称的。 对于一个给定的 $x$ ,将数 $i,i-x$ 两两配对,两者中只能出现一个。 特殊地,$x/2$ 至多只能出现一次。 其余没有配对的数则无限制。 对子的个数是 $\lfloor (x-1)/2\rfloor$ ,而没配对的数的个数是 $k-x+1$。 无标号计数,使用 OGF。 对于至多出现一次的数 : $F_{\rm one}(x)=1+x$ 对于一个对子 : $F_{\rm pair}(x)=1+2x+2x^2+...=\dfrac{1+x}{1-x}$ 对于一个没有配对的数 : $F_{\rm single}(x)=1+x+x^2+...=\dfrac{1}{1-x}$ 将所有生成函数乘起来,答案为 : $$[x^n]\dfrac{(1+x)^{k-x+1+[x\bmod 2=0]}}{(1-x)^{k-x+1+\lfloor (x-1)/2\rfloor}}$$ 考虑如何快速求 $[x^n]\dfrac{(1+x)^a}{(1-x)^b}$ 分子分母的展开式都是容易表示的,暴力计算卷积即可。 $$ \begin{aligned} [x^n]\dfrac{(1+x)^a}{(1-x)^b}&=\sum\limits_{i=0}^n[x^i](1+x)^a[x^{n-i}](1-x)^{-b}\\ &=\sum\limits_{i=0}^n\dbinom{a}{i}\dbinom{b+(n-i)-1}{n-i} \end{aligned} $$ 复杂度 $O(nk)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 2050 using namespace std; const int mod=998244353; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } int fac[MaxN<<1],ifac[MaxN<<1]; int C(int n,int m){ if (m>n)return 0; return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod; } void Init(int n) { fac[0]=1; for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod; ifac[n]=powM(fac[n]); for (int i=n;i;i--) ifac[i-1]=1ll*ifac[i]*i%mod; } int calc(int a,int b,int n) { int ret=0; for (int i=0;i<=n;i++) ret=(ret+1ll*C(a,i)*C(b+(n-i)-1,n-i))%mod; return ret; } int n,k,ans[MaxN]; int main() { scanf("%d%d",&k,&n); Init(n+k); for (int i=2;i<=k+1;i+=2){ ans[i]=calc(i/2,k-i+1+(i-1)/2,n); ans[i+1]=ans[i]; } for (int i=2;i<=k+1;i++)printf("%d\n",ans[i]); for (int i=k;i>=2;i--)printf("%d\n",ans[i]); return 0; } ```