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 }
非做顽石不可,哪管他敬仰暗唾