HDU 6169(数论+DP)

本文介绍了一种解决特定数学问题的算法,该问题要求计算区间[L,R]内最小除数为K的正整数之和,并对结果取模。算法通过预处理素数并使用动态规划方法高效解决此问题。

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



Problem Description

Senior Pan had just failed in his math exam, and he can only prepare to make up for it. So he began a daily task with Master Dong, Dong will give a simple math problem to poor Pan everyday.

But it is still sometimes too hard for Senior Pan, so he has to ask you for help.
Dong will give Pan three integers L,R,K every time, consider all the positive integers in the interval [L,R], you’re required to calculate the sum of such integers in the interval that their smallest divisor (other than 1) is K.

 

Input
The first line contains one integer T, represents the number of Test Cases.
Then T lines, each contains three integers L,R,K(1≤L≤R≤10^11,2≤K≤10^11)
 

Output
For every Test Case, output one integer: the answer mod 10^9+7


 
Sample Input
2 1 20 5 2 6 3
 

Sample Output
Case #1: 5 Case #2: 3
简单想一下就可以发现这个题其实是叫你 令 S<K 求 ( L-1 )/K , ( R )/K 范围内不是 S 的倍数的数(注:所有的 S )
并且 K 必须是素数。
理论上来讲是要容斥一下,但其实是用DP哒(具体有什么联系我不知道请dalao们指出)
这个样子:dp[i][j]=dp[i][j-1]-dp[i/prime[j]][j-1]*prime[j];
( i 是求 1 到 i 的和,j 是第 j 个素数,所以 dp[i][j] 表示小于等于 i 的数中不是 j 以及 j 以前的素数的倍数的所有数之和) 
因为数太大DP数组存不下就小范围打表大范围DFS吧

代码:
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const LL A=2e4+7,B=1e3+7,MAXN=340000+7;
LL prime[MAXN],total;
bool isprime[MAXN];

void make()
{
    LL m=MAXN-3;
    memset(isprime,true,sizeof(isprime));
    isprime[0]=isprime[1]=false;
    total=0;
    for(int i=2;i<=m;i++)
    {
        if(isprime[i])prime[++total]=i;
        for(int j=1;j<=total&&prime[j]*i<=m;j++)
        {
            isprime[i*prime[j]]=false;
            if(i%prime[j]==0)break;
        }
    }
}

LL dp[A][B];
LL L,R,K;


LL dfs(LL n,LL m)
{
    if(m==0)
    {
        LL x=n,y=n+1;
        if(x&1)y/=2;
        else x/=2;
        x%=MOD;y%=MOD;
        return x*y%MOD;
    }
    if(n<=1)return n;
    if(prime[m]>=n&&m>0)return 1;
    if(n<A&&m<B)return dp[n][m];
    LL a=dfs(n,m-1),b=dfs(n/prime[m],m-1)*prime[m];
    b=(b%MOD+MOD)%MOD;
    a=a-b;
    a=(a%MOD+MOD)%MOD;
    return a;
}

int main()
{
    make();
    memset(dp,0,sizeof(dp));
    for(LL i=1;i<A;i++)
    {
        dp[i][0]=i*(i+1)/2;
        for(LL j=1;j<B;j++)
        {
            dp[i][j]=dp[i][j-1]-dp[i/prime[j]][j-1]*prime[j];
            dp[i][j]=(dp[i][j]%MOD+MOD)%MOD;
        }
    }
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&L,&R,&K);
        printf("Case #%d: ",++cas);
        int flag=0;
        for(LL i=2;i*i<=K;i++)
        {
            if(K%i==0)flag=1;
            if(flag)break;
        }
        if(flag)
        {
            puts("0");
            continue;
        }
        LL z=sqrt(R);
        if(K>z)
        {
            if(L<=K&&K<=R)
            {
                printf("%lld\n",K%MOD);
            }
            else puts("0");
            continue;
        }
        int t=0;
        R/=K;L=(L-1)/K;
        for(int i=1;i<=total;i++)
        {
            if(prime[i]>=K)break;
            t++;
        }
        LL a=dfs(R,t)*K,b=dfs(L,t)*K;
        LL ans=a-b;
        ans=(ans%MOD+MOD)%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

/*
22
222222222 777777777 9
222222222 777777777 3
3333333333 9999999999 2
Case #1: 0

Case #2: 268518521
*/



博主在写这个的时候很困,所以有什么错误请谅解
本人蒟蒻,如有错误,还望指正



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值