码迷,mamicode.com
首页 > 其他好文 > 详细

ACM学习历程—HDU5587 Array(数学 && 二分 && 记忆化 || 数位DP)(BestCoder Round #64 (div.2) 1003)

时间:2015-11-28 22:58:34      阅读:384      评论:0      收藏:0      [点我收藏+]

标签:

题目链接: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+12的次方有要求。

但是对于每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/2n/2+1只有当大量出现n%2等于0了才能每次截掉一半。但是如果四个一组的话,每次长度变成1/4,但是最多生成n/4n/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\n", ans);
    }
    return 0;
}
View Code

 

代码:(四分)

技术分享
#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\n", ans);
    }
    return 0;
}
View Code

 

ACM学习历程—HDU5587 Array(数学 && 二分 && 记忆化 || 数位DP)(BestCoder Round #64 (div.2) 1003)

标签:

原文地址:http://www.cnblogs.com/andyqsmart/p/5003587.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!