AtCoder Regular Contest 102 E Stop. Otherwise...

题目链接:atcoder

大意:有\(n\)个骰子,每个骰子上面有\(k\)个数,分别是\(1\text ~ k\),现在求\(\forall i\in[2...2k]\),求出有多少种骰子点数的组合方式使得任意两个骰子的点数之和不等于\(i\),注意不考虑顺序

分析

对每个\(i\)计算答案

我们知道,如果\(1\leq j \leq i\)且\(1\leq i-j \leq i\)那么\(j\)和\(i-j\)最多只能出现一个

我们对于每个\(i\)计算出有多少组\((j,i-j)\)满足上面的限制条件,记为\(cnt\)

那么对于剩下的\(k-2*cnt\)的取值便变得可以出现也可以不出现了

所以骰子点数的出现情况的方案数就是

\[\sum_{q=0}^{cnt}C_{cnt}^q*2^q
\]

接下来考虑对于每一种出现情况的组合数

假设我们当前选了\(q\)个限制组中的元素,我们可以列出下面这样一个方程

\[x_1+x_2+\cdots+x_q+y_1+y_2+\cdots+y_{k-2*cnt}=n
\]

其中\(x1,x2,\cdots,x_q\)表示限制组中元素的出现次数,\(y_1,y_2,\cdots,y_{k-2*cnt}\)表示非限制组中的元素的出现次数

那么依照上面的假设就有\(x_i> 0,y_i\geq 0\),求该方程的解的组数

将方程转化为

\[x_1+x_2+\cdots+x_{cnt}+(y_1+1)+(y_2+1)+\cdots+(y_{k-2*cnt}+1)=n+(k-2*cnt)
\]

那么原问题就变成了一个经典的求方程正整数解的问题了,答案为\(C_{n+k-2*cnt-1}^{k-cnt-1}\)

将两者乘起来即可

注意当\(i\)是偶数的时候,会出现\((i/2,i/2)\)这样的一组无意义的限制组,我们可以强制令其取或不取(取最多也只有1个),然后和上面一样的求解

时间复杂度\(O(n^2)\)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxd=998244353,N=100000;
const double pi=acos(-1.0);
typedef long long ll;
int n,k,c[4010][4010],bin[4010]; int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
} void init()
{
k=read();n=read();
int i,j;
c[0][0]=1;
for (i=1;i<=4000;i++)
{
c[i][0]=1;
for (j=1;j<=i;j++)
c[i][j]=((ll)c[i-1][j]+c[i-1][j-1])%maxd;
}
bin[0]=1;
for (i=1;i<=4000;i++) bin[i]=(bin[i-1]*2)%maxd;
//for (i=1;i<=6;i++) cout << bin[i] << " ";cout << endl;
} ll calc(int x,int y,int z)
{
if (y<0) return 0;
//cout << x << " " << y << " " << z << endl;
int i;ll ans=0;
for (i=0;i<=x&&i<=z;i++)
{
if (((i+y-1)>=0) && ((i+y-1)<=(z+y-1)))
{
//cout << i << " " << x << " " << y << " " << z << endl;
ans=(ans+((1ll*c[x][i]*bin[i])%maxd*c[z+y-1][i+y-1])%maxd)%maxd;
}
}
return ans;
} void work()
{
int i,j;
for (i=2;i<=k*2;i++)
{
ll ans=0;int cnt=0;
for (j=1;j<=k;j++)
{
int tmp=i-j;
if ((tmp<=k) && (tmp>=1)) cnt++;
}
//cout << i << " " << cnt << endl;
if (i&1)
ans=calc(cnt/2,k-cnt,n);
else
ans=(calc((cnt-1)/2,k-cnt,n)+calc((cnt-1)/2,k-cnt,n-1))%maxd;
printf("%lld\n",ans);
}
} int main()
{
init();
work();
return 0;
}
上一篇:ELK 日志分析体系


下一篇:[转]将oracle数据库的编码变成utf-8