Wells有一片神奇的花园,花园里有无数神石和一个神山。

每一天,Wells 可以喂神山一个神石,使得神山的高度变为原高度乘上神石的能力值,或者使得所有的神石能力值+1
第 0 天时,所有神石的能力值为 0,神山的高度为 1
Wells想知道神山最快的长成某一个高度值最少需要多少天
定义函数 F(x)是使得 神山 变成 x 的最少天数
定义函数 G(x,p):若 F(x)<=p 则 G(x,p)=1 否则 G(x,p)=0
为了让问题不那么无聊,Wells想让你计算以下结果
现在让你求∑G(x,p) 其中 l<=x<=r
Input
第一行包含三个正整数 l,r,p l,r<=109,p<=100
Output
一个整数,∑G(x,p) 其中 l<=x<=r
Sample Input
2 10 3
Sample Output
1
Hint
显然只有 2 才能
题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2081
题解:注意是刚好长到x的最小天数,不能超过x。通过暴力搜索,我们发现能够在100天内长到的高度有4e6个左右,把这4e6个离散,离散后是3e6左右个,所以我们应该想办法找到可以长到的高度,然后判断是否在[L,R]区间里,具体的:结果等于加法次数+乘法次数,加法次数等于最大发因子,乘法次数可以用背包求解最优。(充分利用单调性)
(代码做了注释,理解起来不难。)
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <assert.h>
#include <iostream>
using namespace std;
vector<int> prime;
int sz, p;
int L,R;
bool isp(int n)
{
for(int i = 2;i * i <= n;i ++) if(n % i == 0) return 0;
return 1;
}
void init()
{
for(int i = 2;i <= 100;i ++) if(isp(i)) prime.push_back(i);
sz = (int)prime.size();
}
vector<int> v;
void dfs(int now, int x)
{
if(now >= sz) {
v.push_back(x);
return ;
}
for(;;) {
dfs(now + 1, x);
if(x > R / prime[now]) break;
x *= prime[now];
}
}
int main()
{
init();
scanf("%d %d %d", &L, &R, &p);
dfs(0, 1);
sort(v.begin(), v.end());
int vsz = (int)v.size();
// printf("%d\n", (int)v.size());
vector<int> ck(vsz+1, 0), dp(vsz+1, 0x3f3f3f3f);
dp[0] = 0;
for(int i = 2;i <= p; ++ i) { //枚举最大神石的能量
int l = 0;
for(int j = 0;j < vsz; ++ j) {
while(l < vsz && v[l] < i * v[j]) ++l;//dfs的时候保证了v[j]和v[j]*i都存在,所以找到的v[l]一定是第一个等于v[j]*i的数
if(l < vsz) {
assert(v[l] == i * v[j]);
dp[l] = min(dp[l], dp[j] + 1); //dp[l]是乘法的次数 i是最大的因子即增加神石的能量
if(dp[l] + i <= p) ck[l] = 1;
}
}
}
int res = 0;
for(int i = 0;i < vsz;i ++) {
if(v[i] >= L && v[i] <= R && ck[i]) ++ res;
}
printf("%d\n", res);
return 0;
}