MZOJ #71 maple做数学题

 分析

一道不可多得的好题啊

MZOJ数据过水,可以骗80分

好吧我们说正解

首先,k一定是个素数

其次,如果k > 320000(或k * k >= n时,注意 k * k 可能会爆longlong),那么满足条件的数最多只有一个

接下来,我们进入正题,

如题,说明满足条件的数只能含有k及k以上的素数因子

直接线筛可以骗分,但是看看r的范围

不好意思打扰了

还有一个条件:必须含有k

好的,我们该怎么办

数据范围这么大,不可能直接算

不如放宽限制,看这个答案可不可以从其他答案得到

没错,我想到了搜索,把一个大问题变成小问题来解

嗯,接下来,状态

需要一个上限,其实下限不是那么重要,剪掉就好

需要一个状态存素数

需要的部分显然太大,不好考虑

我们考虑不要的部分,也就是小于k的素数的倍数

 

至于素数,线筛就好 

最终答案很好想

 

怎么实现呢,其实交给相同子问题就好,比如,筛掉含prime[m]的数:

嗯,这题可以记忆化

还有一些小细节,看核心:

代码

  1 /**************************
  2 User:Mandy.H.Y
  3 Language:c++
  4 Problem:math
  5 Apgorithm:
  6 **************************/
  7 
  8 #include<bits/stdc++.h>
  9 #define Max(x,y) ((x) > (y) ? (x) : (y))
 10 #define Min(x,y) ((x) < (y) ? (x) : (y))
 11 
 12 using namespace std;
 13 
 14 typedef long long ll;
 15 
 16 const long long maxn = 320000+5;
 17 const long long mod = 1e9 + 7;
 18 const long long ni = 5e8 + 4;
 19 const int N = 10010;
 20 const int M = 110;
 21 
 22 ll l,r,k,cnt=0,ans;
 23 ll dp[N][M];
 24 ll prime[maxn];
 25 bool vis[maxn];
 26 
 27 template<class T>inline void read(T &x){
 28     x = 0;bool flag = 0;char ch = getchar();
 29     while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
 30     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
 31     if(flag) x = -x;
 32 }
 33 
 34 template<class T>void putch(const T x){
 35     if(x > 9) putch(x / 10);
 36     putchar(x % 10 | 48);
 37 }
 38 
 39 template<class T>void put(const T x){
 40     if(x < 0) putchar('-'),putch(-x);
 41     else putch(x);
 42 }
 43 
 44 void file(){
 45     freopen("math.in","r",stdin);
 46     freopen("math.out","w",stdout);
 47 }
 48 
 49 void readdata(){
 50     read(l);read(r);read(k);
 51 }
 52 
 53 void get_prime(){//线性筛 
 54 
 55     vis[0] = 1;
 56     vis[1] = 0;
 57     
 58     for(ll i = 2;i <= k; ++ i){
 59         if(!vis[i]) prime[++cnt] = i;
 60         for(ll j = 1;j <= cnt && prime[j] * i <= k; ++ j){
 61             vis[prime[j] * i] = 1;
 62             if(i % prime[j] == 0) break;
 63         }
 64     }
 65     
 66 }
 67 
 68 ll DFS(ll n,ll m){
 69     //表示1~n减去前m个质数及其倍数后,剩余数的和
 70     //也就是1~n中最小的质因子都比prime[m]大的数的和 
 71     ll ans = 0;
 72     if(n < 2) return n;
 73     //没有比二小的质数 
 74     if(n < N && m < M && dp[n][m]) return dp[n][m];
 75     //记忆化 
 76     if(!m) ans = (1+n) % mod * (n%mod) % mod * ni % mod;
 77     //1~n中所有数的和 
 78     else {
 79         if(prime[m] > n) {
 80             
 81             while(m && prime[m] > n) --m;
 82             //保证prime[m]<n,反正大于n的时候又不能筛 
 83             ans = DFS(n,m); 
 84             
 85         } else ans = (DFS(n,m-1) - 
 86                       DFS(n/prime[m],m-1) % mod * prime[m] % mod + mod) % mod;
 87     }
 88     
 89     if(n < N && m < M) dp[n][m] = ans;
 90     return ans;
 91 }
 92 
 93 void work(){
 94     
 95     get_prime();
 96     
 97     ans = (DFS(r/prime[cnt],cnt-1) % mod *prime[cnt] % mod - 
 98           (DFS((l-1)/prime[cnt],cnt-1)*prime[cnt] % mod) + mod ) % mod;
 99     //看这里,保证了求出了r/k以内所有只含有k及比k大的因子的数的和
100     //且乘以k不会超r,保证了一定含有k,且k为最小 
101     put(ans);
102 }
103 
104 bool Isprime(){
105     for(ll i = 2;i * i <= k; ++ i)
106         if(k % i == 0) return 0;
107     return 1;
108 }
109 
110 int main(){
111 //    file();
112     readdata();
113     
114     if((!Isprime()) || k > r){
115         puts("0");
116         return 0;
117     }
118     
119     if(k >= 320000 || (k * k >= r)){
120         if(k >= l ) put(k % mod);
121         else puts("0"); 
122     } else work();
123     return 0; 
124 }
View Code

 

posted @ 2019-09-02 08:30  Mandy_H_Y  阅读(291)  评论(0编辑  收藏  举报