多校(第四场)C题 数位DP

题目大意是给出一个式子an 求其前n项的和。

首先我们可以发现 在n mod4 为0、3时 an=a[n/2]+1,n mod4为1、2时an=a[n/2]-1。
而从a1到an要经过log2 an次这样的加减,可以转化成二进制位上的问题。
把n化为二进制数组b,当某位bi与bi-1组成的二进制为0、3时+1,为1、2时-1,例如bi==1,bi-1==0,二进制就是10,十进制为2,此时-1。
此时我们就可以在log复杂度内求出an。

然后我们再利用数位dp,来求出前n项的和。

dp存储的第一维len表示位数。
第二维 if pre==2表示bi前一项为空,故不计算bi+1与bi,else pre表示前一位的值。
第三维为sum,表示前面位数累计的和,因为会有负值,故加上64最后再减去并取绝对值。

另外,因为对于+1时的0、3二进制分别为00,11,故我们可以通过bi+1==bi来判断加减。
以下为数位dp的代码。
#include <bits/stdc++.h>
using namespace std;
const long long mod=1000000007;
long long i,n,m,dp[64][3][128],a[64],T;
long long dfs(int len,bool maxi,int pre,int sum)
{
    if(dp[len][pre][sum]!=-1&&maxi==0)return dp[len][pre][sum];
    long long cnt=0;
    if(!len)return abs(sum-64);
    int maxn=maxi?a[len]:1;
    for(int i=0;i<=maxn;i++)
    {
        if(pre==2)
        {
            if(i)cnt+=dfs(len-1,maxi&&i==a[len],i,sum);
            else cnt+=dfs(len-1,maxi&&i==a[len],pre,sum);
        }
        else
        {
            if(i==pre)cnt+=dfs(len-1,maxi&&i==a[len],i,sum+1);
            else cnt+=dfs(len-1,maxi&&i==a[len],i,sum-1);
        }
    }
    cnt%=mod;
    return maxi?cnt:dp[len][pre][sum]=cnt;
}
long long div(long long tmp)
{
    int p=0;
    while(tmp)a[++p]=tmp%2,tmp/=2;
    return dfs(p,1,2,64);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        printf("%lld\n",div(n));
    }
    return 0;
}



全部评论
请问为什么要开一维记录前面sum的值呢,后面的状态不是不会受到前面sum的影响吗?
点赞 回复 分享
发布于 2018-08-01 14:49

相关推荐

小红书 后端选手 n*16*1.18+签字费期权
点赞 评论 收藏
分享
11-27 12:43
已编辑
门头沟学院 C++
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务