5 3 3
3
比赛时一开始只想到30分。
后来突然发现那个k似乎很小。
那么就考虑把每个a都分解成m*p+q的形式。
后者利用DP,前者利用组合数来求一求即可。
然而似乎我打错了,而且时间复杂度估计错误,只拿了30分。(本来能多20分的啊)
上面50分的做法一目了然。
细细来讲就是:
设\(A=\sum q\)
那么当\(A\%m==n\%m\)时,就代表可以对答案造成贡献。
由于每个a都是由若干个m相加最后加上个q形成。
由于q的答案利用DP已经求出来了,接下来考虑如何分配这若干个m。
当然组合数(挡板问题)
由于\(\frac n m\)巨大无比,但是k却奇小无比。
怎么办?考虑直接把那个组合数计算的柿子上下约分即可。
接下来考虑优化。
我们看到DP似乎有点没用,那么考虑再利用组合数来代替DP。
我们发现,这个不能直接套组合数,由于有些a可能分配到了比m-1大的数,所以我们要把这部分的答案容斥一下。
注意小细节即可。
#include <iostream>
#include <cstdio>
#include <cstring>
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);
}
原文:https://www.cnblogs.com/RainbowCrown/p/11350378.html