hdu 5587 Array

  题目链接: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 }
View Code

  网上好像有很多思路可以递归或者递推找规律的,我学习了下,感觉也很强大:

 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 }
View Code

  因为不是比赛时做,所以时间比较充裕,变量名写得有点长了,看着像在写工程代码

  这题,必须好好琢磨才行,它和刚过去的 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 }
View Code

 

posted @ 2015-12-08 19:58  Newdawn_ALM  阅读(183)  评论(0编辑  收藏  举报