您的位置:首页 > 其它

HDU 6169 Senior PanⅡ(数论+dp)

2018-02-12 13:21 295 查看
Description

给出一个区间[L,R][L,R],问该区间中所有以KK作为最小因子(大于11的)的数字之和

Input

第一行输入一整数TT表示用例组数,每组用例输入三个整数L,R,KL,R,K(1≤L≤R≤1011,2≤K≤1011)(1≤L≤R≤1011,2≤K≤1011)

Output

对于每组用例,输出答案,结果模109+7109+7

Sample Input

2

1 20 5

2 6 3

Sample Output

Case #1: 5

Case #2: 3

Solution

显然kk需要是素数,否则不存在数字以KK为最小因子,且如果KK是NN的因子,那么N/KN/K也是NN的因子,进而有K≤N/KK≤N/K,故K≤N−−√K≤N,即如果K>1011−−−−√≈320000K>1011≈320000时,至多有一个数字(即KK本身)以KK为最小因子,当K≤320000K≤320000时,只需解决11~NN中以KK为最小素因子的数字之和即可

考虑dpdp,以dp[i][j]dp[i][j]表示前ii个数字中没有以前jj个素数为最小素因子的数字之和,假设KK是第pos+1pos+1个因子,如果一个数字XX以KK为最小素因子,那么X/KX/K必然不以前pospos个素数为最小素因子,反之,如果YY不以前pospos个素因子为最小素因子,则YKYK必然以KK为最小素因子,那么我们只要求11~⌊NK⌋⌊NK⌋中没有以前pospos个素数为最小素因子的数字之和,乘上KK即为11~NN中以KK为最小素因子的数字之和,且同理可以得到转移方程,前ii个数字中不以前jj个素数为最小素因子的数字之和==前ii个数字中不以前j−1j−1个素数为最小素因子的数字之和−−前ii个数字中以第jj个素数为最小素因子的数字之和,即dp[i][j]=dp[i][j−1]−pj⋅dp[⌊ipj⌋][j−1]dp[i][j]=dp[i][j−1]−pj⋅dp[⌊ipj⌋][j−1]

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define M 320000
#define mod 1000000007
#define inv2 500000004
#define A 8000
#define B 1000
int res=0,p[M+5],mark[M+5],dp[A+5][B+5];
int ins(int x,int y)
{
return x+y>=mod?x+y-mod:x+y;
}
int des(int x,int y)
{
return x-y<0?x-y+mod:x-y;
}
void init()
{
for(int i=2;i<=M;i++)
if(!mark[i])
{
p[++res]=i;
for(int j=2*i;j<=M;j+=i)mark[j]=1;
}
dp[0][0]=0;
for(int i=1;i<=A;i++)
{
dp[i][0]=ins(dp[i-1][0],i);
for(int j=1;j<=B;j++)
dp[i][j]=des(dp[i][j-1],(ll)p[j]*dp[i/p[j]][j-1]%mod);
}
}
bool check(ll n)
{
if(n<=M)return mark
^1;
for(int i=1;i<=res&&(ll)p[i]*p[i]<=n;i++)
if(n%p[i]==0)return 0;
return 1;
}
int Solve(ll n,int m)
{
if(n<2)return n;
if(m==0)
{
n%=mod;
return n*(n+1)%mod*inv2%mod;
}
if(n<=A&&m<=B)return dp
[m];
if(n<=p[m])return 1;
return des(Solve(n,m-1),(ll)p[m]*Solve(n/p[m],m-1)%mod);
}
int main()
{
init();
int T,Case=1;
scanf("%d",&T);
while(T--)
{
ll L,R,K;
scanf("%I64d%I64d%I64d",&L,&R,&K);
printf("Case #%d: ",Case++);
if(!check(K))printf("0\n");
else if(K>M)
{
if(K>=L&&K<=R)printf("%d\n",K%mod);
else printf("0\n");
}
else
{
int pos=1;
while(p[pos]<K)pos++;
pos--;
int ans=des(K*Solve(R/K,pos)%mod,K*Solve((L-1)/K,pos)%mod);
printf("%d\n",ans);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: