HDU 4588 Count The Carries 数位DP || 打表找规律

2013年南京邀请赛的铜牌题。。。做的很是伤心,另外有两个不太好想到的地方。。。。a 可以等于零,另外a到b的累加和比较大,大约在2^70左右。


首先说一下解题思路。

首先统计出每一位的1的个数,然后统一进位。

设最低位为1,次低位为2,依次类推,ans[]表示这一位上有多少个1,那么有

sum += ans[i]/2,ans[i+1] += ans[i]/2;

sum即为答案。

好了,现在问题转化成怎么求ans[]了。


打表查规律比较神奇,上图不说话。


打表的代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 9999991
#define lowbit(x) (x&(-x))

using namespace std;

void out(int x)
{
    while(x)
    {
        printf("%d",x&1);
        x >>= 1;
    }
}

int main()
{
    for(int i = 1;i <= 100; ++i)
    {
        printf("i = %3d : ",i);
        out(i);
        puts("");
    }

    return 0;
}


好了,重头戏来了,这个数位DP做的还是很有成就感的。

记忆化搜索 + bfs推送

昨天其实想这个思路了。。。不过当时比较着急没写出来,心态是个很重要的东西。

记忆化搜索部分dp[sta][site][dig] 第一维表示是否到达上限,site表示第几位,dig表示是1还是0。

那么site == 1时,也就是说最高位记录的信息是正确的,但是其他的就不是想要的了。因为dp[sta][site][sta] 表示当前位到最低位的方案数。

所以说要把最高位的答案推送下去。

推送很简单,每种状态最多只会由两种子状态得到,所以按比例推送一下就行了。

详见代码。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 9999991
#define lowbit(x) (x&(-x))

using namespace std;

_LL dp[2][105][2],dp1[2][105][2];

_LL up[105];

_LL ansa[105],ansb[105];

_LL dfs(_LL sta,_LL site,_LL dig)
{
    if(dp[sta][site][dig] != -1)
        return dp[sta][site][dig];
    if(site == 100)
    {
        dp[sta][site][dig] = 1;
        return 1;
    }

    dp[sta][site][dig] = 0;

    if(sta == 0)
    {
        dp[sta][site][dig] = dfs(0,site+1,0) + dfs(0,site+1,1);
    }
    else
    {
        if(up[site+1] == 1)
        {
            dp[sta][site][dig] = dfs(1,site+1,1) + dfs(0,site+1,0);
        }
        else
        {
            dp[sta][site][dig] = dfs(1,site+1,0);
        }
    }
    return dp[sta][site][dig];
}

void Cal(_LL x,_LL *ans)
{
    _LL temp = x,len = 100;

    memset(ans,0,sizeof(_LL)*(102));

    if(x == 0)
        return ;
    while(temp)
    {
        up[len--] = temp%2;
        temp /= 2;
    }

    len++;
    memset(dp,-1,sizeof(dp));
    dfs(1,len,1);
    dfs(0,len,0);
    _LL i;

    memset(dp1,0,sizeof(dp1));

    dp1[1][len][1] = max((_LL)0,dp[1][len][1]);
    dp1[1][len][0] = max((_LL)0,dp[1][len][0]);
    dp1[0][len][1] = max((_LL)0,dp[0][len][1]);
    dp1[0][len][0] = max((_LL)0,dp[0][len][0]);

    for(_LL site = len;site <= 99; ++site)
    {
        dp1[0][site+1][1] += dp1[0][site][1]*dp[0][site+1][1]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][1] += dp1[0][site][0]*dp[0][site+1][1]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][0] += dp1[0][site][1]*dp[0][site+1][0]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][0] += dp1[0][site][0]*dp[0][site+1][0]/(dp[0][site+1][1] + dp[0][site+1][0]);

        if(up[site+1] == 1)
        {
            if(up[site] == 1)
            {
                dp1[1][site+1][1] += dp1[1][site][1]*dp[1][site+1][1]/(dp[1][site+1][1] + dp[0][site+1][0]);
                dp1[0][site+1][0] += dp1[1][site][1]*dp[0][site+1][0]/(dp[1][site+1][1] + dp[0][site+1][0]);
            }
            else
            {
                dp1[1][site+1][1] += dp1[1][site][0]*dp[1][site+1][1]/(dp[1][site+1][1] + dp[0][site+1][0]);
                dp1[0][site+1][0] += dp1[1][site][0]*dp[0][site+1][0]/(dp[1][site+1][1] + dp[0][site+1][0]);
            }
        }
        else
        {
            dp1[1][site+1][0] = dp1[1][site][0] + dp1[1][site][1];
        }
    }

    for(i = len;i <= 100; ++i)
    {
        ans[i] = max((_LL)0,dp1[1][i][1]) + max((_LL)0,dp1[0][i][1]);
    }
}

int main()
{
    _LL a,b,i;

    while(scanf("%I64d %I64d",&a,&b) != EOF)
    {
        Cal(b,ansb);
        Cal(max((_LL)0,a-1),ansa);

        _LL sum = 0;

        for(i = 100;i >= 1; --i)
        {
            sum += (ansb[i]-ansa[i])/2;
            ansb[i-1] += (ansb[i]-ansa[i])/2;
        }
        printf("%I64d\n",sum);
    }

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值