hdu6169 数论 思维DP 2017多校第九场1009
来源:互联网 发布:c语言编译器vc6.0下载 编辑:程序博客网 时间:2024/06/05 04:32
题意
给定区间[L,R]和一个整数K,问区间内所有满足其最小因子(1除外)为K的数的和。L,R,K的范围是(<=1e11) 结果mod1e9+7
题解
首先,根据唯一分解定理,我们知道一个数一定能分解成若干个素数的幂的乘积。那么我们现在考虑一个数的最小因子为k,这个数应该满足什么条件?显然,首先k必须是素数,否则不可能有数的最小因子是k。其次这个数必须由k及比k大的素数的幂的乘积组成。
我们想到上面之后就应该自然想到要分k为素数和k不为素数考虑。
这里数的范围为1e11,那么我们如何判断这个范围内的一个数是不是素数?这个思想和2017多校第四场的1003就类似了。就是对于n以内的数,[
2017多校第四场的1003
如果k不为素数,没什么说的,结果一定是0。
如果k为素数,那么现在我们要求的就是[l,r]范围里面满足由k及比k大的素数的幂的乘积组成的数的总和。
这里我们又可以分情况讨论了。
如果k>=320000,因为我们求的是由k及比k大的素数的幂的乘积组成的数的总和,所以这个数一定会大于1e11,所以现在我们只需考虑k本身。如果k在[l,r]的范围里面,那么结果肯定就是k%mod,否则就是0。
如果k<320000,现在我们的问题就是快速求出[l,r]范围内由k及比k大的素数的幂的乘积组成的数的总和。我们考虑DP。(至于为什么想到DP,QAQ,比赛的时候没有想到,看了题解才会,所以我也不知道)
我们设dp[i][j]表示[1,l]范围的数被前j个素数筛去后剩下数的总和。(筛去的意思就是[l,r]范围的数中去掉由前j个素数及比之大的素数的幂的乘积组成的数)
那么状态转移方程为:dp[i][j] = dp[i][j-1]-prime[j]*dp[i/prime[j]][j-1]
(prime[j]代表第j个素数)
主要说一下prime[j]*dp[i/prime[j]][j-1],其实就是在[1,i]中由k及比k大的素数的幂的乘积组成的数。dp[i/prime[j]][j-1]就是范围[1,i]里面k的倍数中比k大的倍率的倍数的倍率的和。多体会几遍应该就懂了。
现在剩下最后一个问题。320000范围里素数大约有30000个,l和r的范围为1e11,那么我们dp的数组肯定开不下。所以我们小范围记忆化,大范围搜索。
代码
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 320005;const int len = 1e5+5;const int p_m = 105;const int mod = 1e9+7;ll l,r,k,prime[maxn];int num;ll dp[len][p_m],inv2;bool isprime[maxn];map<ll,int> mp; //mp[i]=j代表i这个素数是第j个素数ll pow_mod(ll a,ll b) //快速幂,用来求逆元{ ll res=1; while(b) { if(b&1) res = res*a%mod; a = a*a%mod; b>>=1; } return res;}void get_Prime(){ num = 0; memset(isprime,true,sizeof(isprime)); for(ll i=2;i<maxn;i++) { if(isprime[i]) { prime[++num] = i; mp[i]=num; } for(int j=1;j<=num&&prime[j]*i<maxn;j++) { isprime[i*prime[j]] = false; if(i%prime[j]==0) break; } }}void init(){ memset(dp,0,sizeof(dp)); for(ll i=1;i<len;i++) { for(int j=0;j<p_m;j++) { if(j==0) { dp[i][j] = (i+1)*i/2; dp[i][j] %= mod; } else { dp[i][j] = dp[i][j-1]-prime[j]*dp[i/prime[j]][j-1]%mod; dp[i][j] = (dp[i][j]+mod)%mod; } } } inv2 = pow_mod(2,mod-2);}ll dfs(ll x,int a){ if(x<len && a<p_m) return dp[x][a]; if(a==0) return x%mod*(x%mod+1)%mod*inv2%mod; if(x<=1) return x; if(a && prime[a]>x) //算是一个小优化,去掉的话会TLE { while(a && prime[a]>x) a--; return dfs(x,a); } return (dfs(x,a-1)-prime[a]*dfs(x/prime[a],a-1)%mod+mod)%mod;}int main(){ int t; get_Prime(); init(); scanf("%d",&t); for(int ca=1;ca<=t;ca++) { scanf("%lld%lld%lld",&l,&r,&k); printf("Case #%d: ",ca); if(k>=maxn) { bool flag = false; for(int i=1;i<=num && prime[i]*prime[i]<=k;i++) { if(k%prime[i]==0) { flag=true; break; } } if(flag) printf("0\n"); else { if(k<=r && k>=l) printf("%lld\n",k%mod); else printf("0\n"); } } else { ll ans; if(mp[k]>0) { ans = dfs(r/k,mp[k]-1)*k%mod-dfs((l-1)/k,mp[k]-1)*k%mod; ans = (ans+mod)%mod; printf("%lld\n",ans); } else printf("0\n"); } } return 0;}
上面代码我觉得最需要注意的就是在计算过程中要时刻注意取模。计算最好都加上(+mod)%mod,不然很可能会出错。
- hdu6169 数论 思维DP 2017多校第九场1009
- hdu6169 Senior PanⅡ【2017多校第九场】
- hdu4689 多校第九场 dp
- hdu5396 2015多校第九场 区间dp
- 多校第九场
- hdu 4398 多校联合赛 第九场 X mod f(x) 数论
- 多校比赛第九场
- [hdu 4945]14多校第八场A 2048 状压DP+数论
- 多校第九场 1005 hdu 5400 Arithmetic Sequence ( dp)
- hdu 5854K-th value (2016多校第九场1011) 树形dp
- 2015多校第九场 HDU 5396 Expression 区间DP,组合数
- 2017多校训练赛第九场 HDU 6170 Two String(dp)
- HDU 4968 Improving the GPA 多校第九场1009
- 2017多校联合第九场/hdu6162(树链剖分)
- hdu 4690 EBCDIC 多校第九场
- 多校第九场Too Simple题解
- 多校第九场Arithmetic Sequence题解
- 2015多校第九,十场总结
- 仿朋友圈图片查看功能
- 1775:采药
- thttpd服务器与CGI的使用——解决CGI中不能使用system()函数的问题
- python之常用的时间格式转换
- 《欢乐捕鱼合集》隐私政策
- hdu6169 数论 思维DP 2017多校第九场1009
- Java基础(五)流程控制语句
- WEKA中文乱码
- Python-单例模式-只初始化一次
- Maven安装教程详解
- (一)SpringBoot入门
- Js-三大事件(鼠标事件、键盘事件、html事件)
- Ubuntu16.04配置apache2+CGI
- 安卓Service