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;
}

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

时间: 2024-10-24 04:23:01

HDU 4588 Count The Carries 数位DP || 打表找规律的相关文章

HDU 4588 Count The Carries(找规律,模拟)

题目 大意: 求二进制的a加到b的进位数. 思路: 列出前几个2进制,找规律模拟. #include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> #include <stack> #include <vector> using namespace std; int main() { int

HDU 4588 Count The Carries (2013年南京邀请赛)

题目地址:HDU 4588 这题是学长跟我说的一道数位DP..然后我就按着数位DP去做的,倒是写出来了,但是一直TLE..后来用类似找规律的方法解出来了.. 首先这题其实就是求每位上总共有多少个1,然后不断从低位开始向高位进位. 方法是比如二进制为1010010的这个数,就可以拆成1000000+10000+10三个数,然后从0到111111就是0和1的一个全排序,那么每一位上的1的个数总和一定是占一半,然后首位+1,那么10000也可以这样算,然后累加起来就好了.但是还要注意的是,首位的1不只

HDU 4588 Count The Carries(数学 二进制 找规律啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4588 Problem Description One day, Implus gets interested in binary addition and binary carry. He will transfer all decimal digits to binary digits to make the addition. Not as clever as Gauss, to make th

Hdu 4588 Count The Carries (规律)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4588 思路: 0     0000 1     0001 2     0010 3     0011 4     0100 5     0101 6     0110 7     0111 8    1000 9    1001 10  1010 观察可知,第i位有(1<<i)个0,与(1<<i)个1,按照长度为(1<<(i+1))长度循环.则对于数n,可以求出1到n中各位

HDU 4588 Count The Carries (数学,计数)

题意:给定两个十进制数,求二进制中,从x加到y的二进制进了多少位. 析:把这些数字的二进制纵向罗列出来,然后一位一位的把和加起来,最终得到总的进位数.从1到x,第i位上1的总数是x左移i+1位再右移i位后得到的, (在第 0位上,1和0以1010101010的周期出现,并且每个周期一个1,在第1位上,1和0以11001100的周期出现,并且每个周期2个1,以此类推,则第n位上的1的个数是x/2^n*2^(n-1),即先左移n+1位,再右移n位),如果x在i位上面上是1,特殊判断一下,求一下周期以

HDU.4352.XHXJ&#39;s LIS(数位DP 状压 LIS)

题目链接 数位DP. 至于怎么求LIS,因为只有10个数,所以可以参照O(nlogn)求LIS的方法,状压记录状态. 每次加一个数和求LIS一样更新状态.最后状态中1的个数就是LIS的长度. //93MS 3004K #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() typedef long long LL; c

hdu 2089 不要62 (数位dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 思路:用变量记录吉利数,和最高位为2的吉利数还有不是吉利数的个数... code: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[10][3]; //dp[i][j] ,i表示位数,j表示状态<pre name="code"

HDU 4352 XHXJ&#39;s LIS 数位DP + 状压

由LIS的nlogn解法 可以得出最后统计数组中数的个数即为LIS的长度 这样就可以状压了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <c

hdu 3709 Balanced Number (数位dp)

Balanced Number Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 1871    Accepted Submission(s): 836 Problem Description A balanced number is a non-negative integer that can be balanced if a pi