题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5587
题目大意就是初始有一个1,然后每次操作都是先在序列后面添加一个0,然后把原序列添加到0后面,然后从0到末尾,每一个都加上1。
例如:a0, a1, a2 => a0, a1, a2, 1, a0+1, a1+1, a2+1
题解中是这么说的:“
其实Ai为i二进制中1的个数。每次变化A{k+2^i}=A{k}+1,(k<2^i)不产生进位,二进制1的个数加1。然后数位dp统计前m个数二进制1的个数,计算每一位对答案的贡献。只需考虑该位填1,其高位与低位的种数即可。
”
不过我没有想到这个。
我写了几次变换后,发现:
对最前面添加一个0,
于是每次变换长度都变成两倍,而且前后序列每一个对应差值为1。
不过这样前后二分显然对于m+1是2的次方有要求。
但是对于每2个组成一组,那么发现,至少每次变换都是以2的倍数个变换的。
也就是说单看i%2== 1的那些数ai,发现他们组成的序列变换和原序列一模一样。
i%2== 0的同理,不过需要在每一个数的基础上加上1。
然后对于s(n),自然可以由它前面i%2 == 1, i%2 == 0的两组序列构成
于是就变成了s(n) = s(n/2)+s(n/2)+n/2 or s(n/2+1)+s(n/2)+n/2(取决于n%2)
这样的话就能二分下去了,不过需要记忆化,这里采用了map进行记忆化。
不过比赛的时候,我写的是四个为一组。由于上面的n/2和n/2+1只有当大量出现n%2等于0了才能每次截掉一半。但是如果四个一组的话,每次长度变成1/4,但是最多生成n/4和n/4+1。不过这两种在不记忆化的情况下都会T。
不过用map记忆化后,我怕会MLE,本地测了好几组数据,都没有占很大内存。
代码:(二分)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long using namespace std; LL m; map<LL, LL> s; LL dfs(LL n) { if (n == 1) return 0; if (n == 2) return 1; LL ans, t1 = 0, t2; if (n%2) { if (s[n/2+1] == 0) { t1 = dfs(n/2+1); s[n/2+1] = t1; } else t1 = s[n/2+1]; } if (s[n/2] == 0) { t2 = dfs(n/2); s[n/2] = t2; } else t2 = s[n/2]; ans = (n%2)*t1+(2-n%2)*t2; ans += n/2; return ans; } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 1; times <= T; ++times) { scanf("%I64d", &m); LL ans; ans = dfs(m+1); printf("%I64d ", ans); } return 0; }
代码:(四分)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long using namespace std; LL m; map<LL, LL> s; LL dfs(LL n) { if (n == 1) return 0; if (n == 2) return 1; if (n == 3) return 2; if (n == 4) return 4; LL ans, t1 = 0, t2; if (n%4) { if (s[n/4+1] == 0) { t1 = dfs(n/4+1); s[n/4+1] = t1; } else t1 = s[n/4+1]; } if (s[n/4] == 0) { t2 = dfs(n/4); s[n/4] = t2; } else t2 = s[n/4]; ans = (n%4)*t1+(4-n%4)*t2; ans += n/4*4; if (n%4) ans += n%4-1; return ans; } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 1; times <= T; ++times) { //s.clear(); scanf("%I64d", &m); LL ans; ans = dfs(m+1); printf("%I64d ", ans); } return 0; }