【数论】君と彼女の恋

本文探讨了一种特定的数学问题:寻找满足条件的非空非负整数序列的个数,这些序列的元素和为给定值n,且每个元素对m取模的结果各不相同。文章详细介绍了如何使用动态规划解决此问题,并提供了完整的代码实现。

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

神 「肯定 。美雪 准备了两个 变数 ,记它们为 n和m」

神 「找到一个非 空的非负整数序列 S,满足 S的所有元素之和为 n,而且每个元素对 m取模得到的结果都不相同 」

神 「这种 序列的个数 ,对神的电话号码取模, 就是 命运的数字 」

更加 听不明白 了…… 但是 你能懂的吧。

【嗯…… 怎样 的两个序列算是不同的?】

神 「只要长度不相同,或者 存在 一位 不同 的元素,即为不同序列 」

对于 100% 的数据, n≤10^18,m≤100。

鸽笼原理,数列长度最多为100,考虑状压DP,用fij表示余数选出i个时和为j的方案数,我们可以枚举每个余数,假如选出了cnt个余数,如果n-所有余数之和tot能被m整除,那么这个余数集合合法,接下来给每个数加上m的非负整数倍,并求这样的集合的方案数,等于把(n-tot)/m个无标记小球放入cnt个箱子中,用隔板法求方案数为C(n-tot+cnt-1)(cnt-1),所以这个合法的余数集合的方案数为cnt的阶乘乘上前面那个方案数,然而这还是会T,所以我们不考虑选出哪一个余数,因为只有序列长度和对应的余数和才会影响答案,所以我们用fij来表示集合长度为i,余数和为j的方案数,用类似背包的转移就可以A了,总复杂度O(m^4)

#include<cctype>
#include<cstdio>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<algorithm>
#define mod 905229641
#define LL long long
using namespace std;
LL read()
{
    LL 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;
}
LL f[100][5100],n,m;
LL q,an;

void bei()
{
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for (int k=0;k<m;k++)
                for (int i=k;i>=0;i--)
                        for (int j=k*(k-1)/2;j>=0;j--)
        {
                f[i+1][j+k]=(f[i+1][j+k]+f[i][j])%mod;
        }
}
/*
void gcd(LL a,LL b)
{
        if (b==0)
        {
                x=1;
                y=0;
                return;
        }
        gcd(b,a%b);
        LL t=x;
        x=y%mod;
        y=(t-a/b*y%mod)%mod;
}

LL multi(LL a,LL b,LL c)
{
        int ans=1;
        a=a%c;
        while(b>0)
        {
                if(b%2==1)
                ans=(ans*a)%c;
                b=b/2;
                a=(a*a)%c;
        }
        return ans;
}
*/

int main()
{
        n=read();m=read();
        int sum=m*(m-1)/2;
        bei();
        LL ans=0;
        for (int i=0;i<=sum;i++)
                if ((n-i)%m==0)
        {
                for (int j=1;j<=m;j++)
                        {
                                LL xx=(n-i)/m;
                                an=(xx+j-1)%mod;
                                int sum=1;
                                 for (int k=1;k<j;k++)
                                {
                                        sum=sum*an % mod;
                                        an--;
                                }
                                sum=sum*(j*f[j][i]%mod)%mod;
                                ans=(ans+sum)%mod;
                        }
//                                cout<<an<<' '<<q<<' '<<f[k][i]<<' '<<k<<' '<<i<<endl;
        }
        cout<<ans<<endl;
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值