官方题解。
https://www.nowcoder.com/discuss/21599
1. 超级素数幂
如果一个数字能表示为p^q(^表示幂运算)且p为一个素数,q为大于1的正整数就称这个数叫做超级素数幂。现在给出一个正整数n,如果n是一个超级素数幂需要找出对应的p,q。
分析:看完题目,p要求是素数,q是大于1的整数,如果暴力枚举素数的话,由于 输入一个正整数n(2 ≤ n ≤ 10^18), 这个数很大,无法枚举所有小于它的素数,复杂度太高,所以只能从q入手,考虑2^64 > 10^18,q的范围只能是2到64,这个范围很小,然后求解,但是确定q以后怎么求解,这就需要开q次方,使用pow,得到结果,然后检查,是否q次幂后相等,是否是素数,返回结果。
有2道题目的套路跟这道题目差不多:
https://code.google.com/codejam/contest/5264487/dashboard#s=p1
https://leetcode.com/problems/smallest-good-base/
想到开幂次的方法,理解后很容易做出来。
1 /* 2 ID: y1197771 3 PROG: test 4 LANG: C++ 5 */ 6 #include<bits/stdc++.h> 7 #define pb push_back 8 #define FOR(i, n) for (int i = 0; i < (int)n; ++i) 9 #define dbg(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl 10 typedef long long ll; 11 using namespace std; 12 typedef pair<int, int> pii; 13 const int maxn = 1e3 + 10; 14 15 ll f(ll x, ll p) { 16 ll r = 1; 17 while(p--) { 18 r *= x; 19 } 20 return r; 21 } 22 bool check(ll x) { 23 for (ll i = 2; i * i <= x; i++) { 24 if(x % i == 0) return 0; 25 } 26 return 1; 27 } 28 void solve() { 29 ll x; 30 cin >> x; 31 //cout << x << endl; 32 for (int i = 2; i <= 64; i++) { 33 ll d = floor(pow(x, 1.0 / i)); 34 //cout << d <<endl; 35 if(d < 2) continue; 36 if(!check(d)) continue; 37 ll t = f(d, i); 38 if(t == x) { 39 cout << d << " " << i << endl; 40 return; 41 } 42 43 } 44 cout << "No" << endl; 45 46 } 47 int main() { 48 //freopen("test.in", "r", stdin); 49 //freopen("test.out", "w", stdout); 50 ios::sync_with_stdio(0); 51 cin.tie(0); cout.tie(0); 52 solve(); 53 return 0; 54 }
2. 序列和
链接:https://www.nowcoder.com/questionTerminal/46eb436eb6564a62b9f972160e1699c9
来源:牛客网
给出一个正整数N和长度L,找出一段长度大于等于L的连续非负整数,他们的和恰好为N。答案可能有多个,我我们需要找出长度最小的那个。
例如 N = 18 L = 2:
5 + 6 + 7 = 18
3 + 4 + 5 + 6 = 18
都是满足要求的,但是我们输出更短的 5 6 7
输入数据包括一行: 两个正整数N(1 ≤ N ≤ 1000000000),L(2 ≤ L ≤ 100)
分析:我没有做出来,其实这道题目挺简单的, 一般首先想到前n想和n*(n+1)/2,然后考虑怎么入手。考虑到长度2<=l<=100,然后枚举长度,长度为l的和为l*(l+1)/2+k*l,这里要求k*l能被l整除,k是整数,然后直接输出结果就可以!注意题目数组可以从0开始,所以和就发生变化了,为n*(n-1)/2。 关键点是发现l很小,以及求和的平移效果。
1 /* 2 ID: y1197771 3 PROG: test 4 LANG: C++ 5 */ 6 #include<bits/stdc++.h> 7 #define pb push_back 8 #define FOR(i, n) for (int i = 0; i < (int)n; ++i) 9 #define dbg(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl 10 typedef long long ll; 11 using namespace std; 12 typedef pair<int, int> pii; 13 const int maxn = 1e3 + 10; 14 void out(int x, int y) { 15 for (int i = 0; i < y; i++) 16 cout << x + i << " "; 17 cout << endl; 18 } 19 void solve() { 20 int n, l; 21 cin >> n >> l; 22 for (int i = l; i <= 100; i++) { 23 if(n - i * (i - 1) / 2 >= 0 &&(n - i * (i - 1) / 2) % i == 0) { 24 int b = (n - i * (i - 1) / 2) / i; 25 26 for (int j = 0; j < i; j++) { 27 if(j != 0) cout << " "; 28 cout << b + j; 29 } 30 cout << endl; 31 return; 32 } 33 } 34 cout << "No" << endl; 35 } 36 int main() { 37 //freopen("test.in", "r", stdin); 38 //freopen("test.out", "w", stdout); 39 ios::sync_with_stdio(0); 40 cin.tie(0); cout.tie(0); 41 solve(); 42 return 0; 43 }
3. 页码统计
输入包括一个整数n(1 ≤ n ≤ 1,000,000,000)
牛牛新买了一本算法书,算法书一共有n页,页码从1到n。牛牛于是想了一个算法题目:在这本算法书页码中0~9每个数字分别出现了多少次?
分析:这个我也没解出来,看完题目,应该是以前见过类似的题目,但是想不起来,看题解,才想到是leetcode上面数1的个数那道题目。
https://leetcode.com/problems/number-of-digit-one/?tab=Description
但是,这个可以通过分析的出来,我对这类题目的想法是(这类型的题目挺多的,下面总结一下):首先这个题目统计0-9,每个数字可以简化简化成只考虑1的个数(0比较特殊,需要单独处理一下),然后调用10次就可以了!然后对于单个数字,考虑到1-n,n的长度是有限的,我们可以预处理出小于10的个数,小于100的个数,小于1000的个数。。。这个很容易通过找规律得出来。
下面考虑具体的数字,比如数342里面1的个数,首先是3,结果包括小于100的个数,该位分别为1,2的个数,接着是考虑十位数字4,也是该位为0,1,2,3的情况,4的情况单独考虑,最后是个位数,最后把所有的结果加起来即可。
贴一下我写的代码,有点丑:
1 /* 2 ID: y1197771 3 PROG: test 4 LANG: C++ 5 */ 6 #include<bits/stdc++.h> 7 #define pb push_back 8 #define FOR(i, n) for (int i = 0; i < (int)n; ++i) 9 #define dbg(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl 10 typedef long long ll; 11 using namespace std; 12 typedef pair<int, int> pii; 13 const int maxn = 1e3 + 10; 14 15 ll f[15], s[15]; 16 ll fz[15], sz[15]; 17 void init() { 18 f[1] = 1; s[1] = 1; 19 ll b = 1; 20 fz[1] = 0; 21 //cout << 1 << " " << f[1] <<" " << s[1] << endl; 22 for (int i = 2; i <= 12; i++) { 23 b *= 10; 24 f[i] = b + 9 * s[i - 1]; 25 s[i] = s[i - 1] + f[i]; 26 fz[i] = 9 * s[i - 1]; 27 sz[i] = sz[i - 1] + fz[i]; 28 //cout << i << " " << f[i] <<" " << s[i] << " " << fz[i] <<" " << sz[i] << endl; 29 } 30 } 31 ll tar; 32 ll res; 33 ll ppow(ll b, ll n) { 34 ll r = 1; 35 while(n > 0) { 36 r *= b; 37 n--; 38 } 39 return r; 40 } 41 void work(int x) { 42 ll t = tar; 43 ll b = 1; 44 int len = 0; 45 while(t > 0) { 46 len++; 47 t /= 10; 48 } 49 b = ppow(10, len - 1); 50 //cout << b << " " << len <<endl; 51 t = tar; 52 while(t > 0) { 53 int d = t / b; 54 //if(d == 0) continue; 55 res += s[len - 1]; 56 res += s[len - 1] * (d - 1); 57 if(d > x) { 58 res += b; 59 } else if(d == x) { 60 res += t % b + 1; 61 } 62 t %= b; 63 b /= 10; 64 len--; 65 } 66 //cout << res << endl; 67 } 68 void work() { 69 ll t = tar; 70 ll b = 1; 71 int len = 0; 72 while(t > 0) { 73 len++; 74 t /= 10; 75 } 76 b = ppow(10, len - 1); 77 //cout << b << " " << len <<endl; 78 t = tar; 79 int x = 0; 80 bool f = 1; 81 while(len > 0) { 82 int d = t / b; 83 //if(d == 0) continue; 84 85 if(f) { 86 res += sz[len - 1]; 87 res += (d - 1) * s[len - 1]; 88 f = 0; 89 } else { 90 res += d * s[len - 1]; 91 if(d == 0) 92 res += t % b + 1; 93 else res += b; 94 } 95 //res += sz[len - 1]; 96 //res += s[len - 1] * (d - 1); 97 //if(d > x) { 98 // res += b; 99 //} else if(d == x) { 100 // res += t % b + 1; 101 //} 102 t %= b; 103 b /= 10; 104 len--; 105 //cout << d << " " << res << " " << sz[len] << endl; 106 } 107 //cout << res << endl; 108 } 109 void solve() { 110 init(); 111 cin >> tar; 112 //tar = 100; 113 work(); 114 cout << res; 115 //return; 116 //cout << func(tar) <<endl; 117 //cout << work(); 118 for (int i = 1; i < 10; i++) { 119 res = 0; 120 work(i); 121 cout << " " << res; 122 } 123 cout <<endl; 124 125 } 126 127 int main() { 128 //freopen("test.in", "r", stdin); 129 //freopen("test.out", "w", stdout); 130 ios::sync_with_stdio(0); 131 cin.tie(0); cout.tie(0); 132 solve(); 133 return 0; 134 }
官方题解的代码比较优美:
考虑可以化简的情况,缩减问题的规模,如349,末尾包括0-9的情况,可以简化为统计34的情况,要乘以10,然后是末尾的统计,发生的次数为34,还包括单独发生的次数。
1 /* 2 ID: y1197771 3 PROG: test 4 LANG: C++ 5 */ 6 #include<bits/stdc++.h> 7 #define pb push_back 8 #define FOR(i, n) for (int i = 0; i < (int)n; ++i) 9 #define dbg(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl 10 typedef long long ll; 11 using namespace std; 12 typedef pair<int, int> pii; 13 const int maxn = 1e3 + 10; 14 15 vector<int> work(int x) { 16 vector<int> res(10, 0); 17 if(x < 1) return res; 18 int t = x % 10; 19 if(t != 9) { 20 res = work(x - 1); 21 while(x > 0) { 22 t = x % 10; 23 x /= 10; 24 res[t]++; 25 } 26 return res; 27 } 28 vector<int> v = work(x / 10); 29 for (int i = 0; i < 10; i++) { 30 res[i] = v[i] * 10 + x / 10 + (i > 0); 31 } 32 return res; 33 } 34 void solve() { 35 int x; 36 cin >> x; 37 vector<int> a = work(x); 38 for (int i = 0; i < 10; i++) { 39 if(i != 0) cout << " "; 40 cout << a[i]; 41 } 42 cout << endl; 43 } 44 int main() { 45 freopen("test.in", "r", stdin); 46 //freopen("test.out", "w", stdout); 47 ios::sync_with_stdio(0); 48 cin.tie(0); cout.tie(0); 49 solve(); 50 return 0; 51 }
这个比较简单,而且容易编写,一般不会出错,上面的找规律就比较麻烦一些。
下面是一些类似的题目:
https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/?tab=Description 440. K-th Smallest in Lexicographical Order,这个题目算是比较经典
https://leetcode.com/problems/nth-digit/?tab=Description
http://www.cnblogs.com/y119777/p/6222408.html
http://mp.weixin.qq.com/s/fYdPJJ0whWVKEC3aiaclPQ 这个也挺有意思的
https://leetcode.com/problems/lexicographical-numbers/?tab=Description
就是这样吧,还有一些可能有点印象,但是想不起来了,总之就是一个是找规律,一个是缩减问题规模,转化为复杂度比较小的问题进行求解。