CF582D

题目大意

给定 P,K,N ,求出有多少对 x,y ,满足 xyN CxymodpK=0

数据范围

N101000
1P,K109
P 为质数

题解

首先,

Cxy=y!x!(yx)!

并且可以发现的是,设 a 为最大的,满足n!modpa=0,那么有
a=np+np2++npw ,其中满足 pwn<pw+1

那么事实上最大的 a 满足Cxymodpa=0,有

a=i=1wypi(xpi+yxpi)

那么假如给定了 x,y ,我们只需要判断是否 ka 即可。

我们再仔细观察,假如我们将 x,y 都转化为 p 进制,那么ypi相当于将后 i 位去除而已!并且可以发现的是,ypi(xpi+yxpi)的取值,只有0或1。取1当且仅当对于 i1 x+(yx) 发生了进位操作!

那么这题就可以做了。

我们设 Fi,j,eq,rem 表示当前由大到小,要确定第 i 位,a的值为 j eq表示当前是否与 N 相同(其实就是数位Dp的思想),rem表示这一位是否要求进位。
一种简单的转移就是枚举下一位是否要进位, x,y 这一位取的数字,然后判断是否合法即可。但这样太慢了,是 O(P2) 的。
但再想一下,没有必要枚举 x,y 取得数字,可以直接用公式计算出来,具体的自己写几个数字就可以发现了,类似于等差数列求和,注意要特殊考虑进位的情况。
并且我们一开始需要将 N 转化为P进制操作。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
const int MAXN = 3505,Mo = int(1e9) + 7;

char B[MAXN];
int F[MAXN][MAXN][2][2];
int A[MAXN],p,a,L;

int Divide(char *a,int p)
{
    int rem = 0;
    for(int i = L;i;i --)
    {
        LL bk = rem * 10LL + a[i];
        rem = bk % p;
        a[i] = bk / p;
    }
    for(;L && !a[L];L --);
    return rem;
}

int Mul(int a,int b)
{
    return a * 1ll * b % Mo;
}

int Dec(int a,int b)
{
    return (a - b + Mo) % Mo;
}

void Inc(int &a,int b)
{
    a = (a + b) % Mo;
}

int Get_P(int c,int r,int nr)
{
    if (r) return Dec(p,Get_P(c,0,nr));
    return c - nr + 1;
}

int Get_S(int c,int r,int nr)
{
    if (r) return Dec(Mul(c + 1,p),Get_S(c,0,nr));
    if (c & 1) return Dec(Mul(c + 2,(c + 1) >> 1),nr * (c + 1));
    return Dec(Mul(c + 1,(c + 2) >> 1),nr * (c + 1));
}

int main()
{
    //freopen("data.in","r",stdin),freopen("data.out","w",stdout);
    scanf("%d%d", &p, &a);
    scanf("%s", B + 1);
    L = strlen(B + 1);
    for(int i = 1;i <= L;i ++) B[i] = B[i] - '0';
    reverse(B + 1,B + L + 1);
    int n = 0;
    for(;L;)
        A[++ n] = Divide(B,p);
    reverse(A + 1,A + n + 1);
    if (a > n) {printf("0\n");return 0;}
    F[1][0][1][0] = 1;
    for(int i = 1;i <= n;i ++)
        for(int j = 0;j <= a;j ++)
            for(int eq = 0;eq < 2;eq ++)
                for(int rem = 0;rem < 2;rem ++)
                    if (F[i][j][eq][rem])
                    {
                        int cur = F[i][j][eq][rem];
                        for(int nr = 0;nr < 2;nr ++)
                        {
                            int nj = min(j + nr,a);
                            if (!eq)
                                Inc(F[i + 1][nj][eq][nr],Mul(cur,Get_S(p - 1,rem,nr))); else
                                {
                                    Inc(F[i + 1][nj][eq][nr],Mul(cur,Get_P(A[i],rem,nr)));
                                    if (A[i]) Inc(F[i + 1][nj][0][nr],Mul(cur,Get_S(A[i] - 1,rem,nr)));
                                }
                        }
                    }
    int ans = 0;
    for(int eq = 0;eq < 2;eq ++)
        Inc(ans,F[n + 1][a][eq][0]);
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值