jzoj6300. Count

本文分享了一道算法竞赛题目,详细解析了从初步思路到最终优化方案的全过程。通过分解与组合数策略,结合DP优化,实现了高效解题。特别关注了细节处理与时间复杂度的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Time Limits: 1000 ms Memory Limits: 524288 KB
Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

5 3 3

Sample Output

3

Data Constraint

在这里插入图片描述

赛时

比赛时一开始只想到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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值