jzoj6300. Count

Time Limits: 1000 ms Memory Limits: 524288 KB

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

5 3 3

Sample Output

3

Data Constraint

jzoj6300. Count_第1张图片

赛时

比赛时一开始只想到30分。
后来突然发现那个k似乎很小。
那么就考虑把每个a都分解成m*p+q的形式。
后者利用DP,前者利用组合数来求一求即可。
然而似乎我打错了,而且时间复杂度估计错误,只拿了30分。(本来能多20分的啊)

题解

上面50分的做法一目了然。
细细来讲就是:
A = ∑ q A=\sum q A=q
那么当 A % m = = n % m A\%m==n\%m A%m==n%m时,就代表可以对答案造成贡献。
由于每个a都是由若干个m相加最后加上个q形成。
由于q的答案利用DP已经求出来了,接下来考虑如何分配这若干个m。
当然组合数(挡板问题)
由于 n m \frac n m mn巨大无比,但是k却奇小无比。
怎么办?考虑直接把那个组合数计算的柿子上下约分即可。

接下来考虑优化。
我们看到DP似乎有点没用,那么考虑再利用组合数来代替DP。
我们发现,这个不能直接套组合数,由于有些a可能分配到了比m-1大的数,所以我们要把这部分的答案容斥一下。
注意小细节即可。

标程

#include 
#include 
#include 
using namespace std;

const long long mo=998244353;

long long n,m,ny2,ans,my,ii,kk,x;
long long t,y,a,b,k,gs;
long long zs[10000];
bool bz[10000];
long long jc[10000001];

__attribute__((optimize("-O3")))
long long qsm(long long a,long long b)
{
	long long t=1;
	long long y=a;
	while (b>0)
	{
		if (b%2==1) t=t*y%mo;
		y=y*y%mo;
		b=b/2;
	}
	return t;
}
__attribute__((optimize("-O3")))
int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%lld%lld%d",&n,&m,&k);
	jc[0]=1;
	for (int i=1;i<=10000000;i++)
	{
		jc[i]=jc[i-1]*i%mo;
	}
	ny2=qsm(2,mo-2);
	for (long long i=1;i<=(m-1)*k;i++)
	{
		if (i%m==n%m)
		{
			long long j=(n/m-i/m);
			my=(jc[i-1]*qsm(jc[k-1]*jc[i-k]%mo,mo-2)%mo);
			x=-1;
			for (long long now=1;now<=k-1;now++)
			{
				ii=i-now*(m-1);
				if (ii<=0) break;
				kk=(jc[k]*qsm(jc[now]*jc[k-now]%mo,mo-2)%mo)*(jc[ii-1]*qsm(jc[k-1]*jc[ii-k]%mo,mo-2)%mo)%mo;
				my=(my+x*kk+mo)%mo;
				x=x*(-1);
			}
			if (j==0)
			{
				ans+=my;
				continue;
			}
			long long op=1;
			for (long long l=1;l<=k-1;l++)
			{
				op=(op*(l%mo))%mo;
			}
			long long oq=1;
			for (long long l=j+1;l<=j+k-1;l++)
			{
				oq=(oq*(l%mo))%mo;
			}
			op=qsm(op,mo-2);
			op=op*oq%mo;
			if (k==1)
			{
				op=1;
			}
			ans=(ans+(op*my)%mo)%mo;
		}
	}
	printf("%lld\n",ans);
}

你可能感兴趣的:(数学杂论,组合数)