题目链接:hdu 5587
前两周 bc 上的题了,因为赶大作业所以没有去打,看了下官方给出的思路,感觉好强大~~竟然能转化成求二进制数 1 的个数:
然后数位 dp 就行了,
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 #define For(i,s,t) for(int i = s; i <= t; ++i) 7 8 ull C[60][60]; // 组合数 9 inline void init_C(int n) { 10 For(i,0,n) C[i][0] = 1; 11 For(i,1,n) For(j,1,i) { 12 C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; 13 } 14 } 15 16 // dp[i] 表示所有 i 位二进制数里'1'的个数总数,p2[i] 即 i 位二进制数的总个数 17 ull dp[60], p2[60] = {1, 2}; 18 inline void init(int n = 56) { 19 init_C(n); 20 for(int i = 1; i <= n; ++i) 21 for(int j = 1; j <= i; ++j) 22 dp[i] += j * C[i][j]; 23 For(i,2,n) 24 p2[i] = p2[i - 1] * 2; 25 } 26 27 int digit[60]; // digit 数组保存的是 x 的二进制表示法 28 ull solve(ull x) { 29 int len = 0; 30 while(x) { 31 digit[++len] = x % 2; 32 x /= 2; 33 } 34 int num1 = 0; // num1 记录的是 digit 里第 i 位前面的'1'的个数 35 ull res = 0; 36 for(int i = len; i; --i) { 37 // 只处理第 i 位为'1'的情况就行,'0'的话在该位没有比它更小的了,所以不用处理 38 if(digit[i] == 1) { 39 // p2[i - 1] * num1 统计的是第 i 位前面的'1'的个数总数(因为此时第 i 位取'0',后面的 i-1 位可以任取) 40 // dp[i - 1] 统计后面 i-1 位里'1'的个数总数 41 res += p2[i - 1] * num1 + dp[i - 1]; 42 ++num1; 43 } 44 } 45 return res; 46 } 47 48 int main() { 49 int t; 50 ull m; 51 init(); 52 scanf("%d",&t); 53 while(t--) { 54 scanf("%I64u",&m); 55 printf("%I64u\n",solve(m + 1)); // 因为上面统一处理的是比 x 小的数,所以在这里 +1 56 } 57 return 0; 58 }
网上好像有很多思路可以递归或者递推找规律的,我学习了下,感觉也很强大:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 const int N = 103; 7 const ull maxLen = 1e16 + 8; 8 9 ull preLen[N], preSum[N]; 10 11 inline void init(int n = 101) { 12 preLen[1] = preSum[1] = 1; 13 for(int i = 2; i <= n; ++i) { 14 preLen[i] = preLen[i - 1] * 2 + 1; 15 preSum[i] = preSum[i - 1] * 2 + (preLen[i] - preLen[i - 1]); 16 if(preLen[i] > maxLen) return ; 17 } 18 } 19 20 ull dfs(ull mlen) { 21 if(mlen == 0) return 0; 22 int pos; 23 for(int i = 1; i <= 101; ++i) { 24 if(mlen < preLen[i + 1]) { 25 pos = i; 26 break; 27 } 28 } 29 ull remainLen = (mlen == preLen[pos] ? 0: mlen - preLen[pos] - 1); 30 return preSum[pos] + dfs(remainLen) + (mlen - preLen[pos]); 31 } 32 33 int main() { 34 init(); 35 int t; 36 ull m; 37 scanf("%d",&t); 38 while(t--) { 39 scanf("%I64u",&m); 40 printf("%I64u\n",dfs(m)); 41 } 42 return 0; 43 }
因为不是比赛时做,所以时间比较充裕,变量名写得有点长了,看着像在写工程代码
这题,必须好好琢磨才行,它和刚过去的 2015CCPC 南阳的热身赛的第一题很像,都是递归的思想,当时现场想不出来,却被身旁的师弟秒掉了,好惭愧的感觉 -_-|| 看来自己还要勤加练习啊,弱渣就需多努力~
顺便贴一下 5586 Sum 的代码,自己1A掉的:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int N = 100005; 6 #define For(i,s,t) for(int i = s; i <= t; ++i) 7 8 int a[N], b[N]; 9 10 inline int trans(const int &x) { 11 return (1890 * x + 143) % 10007; 12 } 13 14 int maxSum(int *b, int n) { 15 int res = b[1], cur = b[1]; 16 for(int i = 2; i <= n; ++i) { 17 cur = max(cur + b[i], b[i]); 18 res = max(res, cur); 19 } 20 return max(res, 0); 21 } 22 23 int main() { 24 int n; 25 while(~scanf("%d",&n)) { 26 int sum = 0; 27 For(i,1,n) { 28 scanf("%d",a + i); 29 sum += a[i]; 30 b[i] = trans(a[i]) - a[i]; 31 } 32 printf("%d\n", sum + maxSum(b, n)); 33 } 34 return 0; 35 }