@2018 牛客网暑期ACM多校训练营(第四场):C: Chiaki Sequence Reloaded(数位DP)

链接:https://www.nowcoder.com/acm/contest/142/C
来源:牛客网
 

题目描述

Chiaki is interested in an infinite sequence a1, a2, a3, ..., which defined as follows:

Chiaki would like to know the sum of the first n terms of the sequence, i.e. . As this number may be very large, Chiaki is only interested in its remainder modulo (109 + 7).

输入描述:

There are multiple test cases. The first line of input contains an integer T (1 ≤ T ≤ 105), indicating the number of test cases. For each test case:
The first line contains an integer n (1 ≤ n ≤ 1018).

输出描述:

For each test case, output an integer denoting the answer.

示例1

输入

复制

10
1
2
3
4
5
6
7
8
9
10

输出

复制

0
1
2
2
4
4
6
7
8
11

 

[思路]

S(n) = (-1)^{\frac{n(n+1)}{2}}   :  1,3,6,10,15,21;  可以发现, 以4 为周期, 前两项 使 S(n) 为 -1 , 后两项 使S(n) 为 1

然后 在看通项:

a_{n}a_{n/2}  + S(n) 

     = a_{n/4}+ S(n/2) + S(n)

     = .....

     =  S(n) + S(n/2) + S(n/4) + S(n/8) +..... +a[1];  a[1] =  0;

对于 an ;  我们看 二进制下形式;  例如 12 : 1100   = S(1100) + S(110) + S(11)    统计末位 相邻的两位: 如果 是 10 或者 01 

则使 S(x) = -1,  else  S(x) = 1;

故得出一个结论 :  an = \sum(所有相邻的两位相同的个数) -\sum(所有相邻的两位不相同的个数)

然后 构建一个 完全二叉树

 然后 利用 数位DP  ,DFS 搜每一个 二进制位,  相当于把1-n,全都扫描了一遍 ,

记录 前一个 front  与 现在这个 , 以及 前一个的limit  做限制 ,跑DFS,在

如果 前一个 为 0 , 那么 后面的数 最大可以为 1, 如果 前面为1, 则后面的数要在限制之内

回溯的时候记录答案

 

[代码]

#include <iostream>
#include <bits/stdc++.h>

#define rep(i,a,n) for(int i = a; i < n ; i++)
#define per(i,a,n) for(int i = n-1; i>=a;i--)

typedef long long ll;

using namespace std;
const int mod = 1e9+7;

ll dp[100][3][200];

int dig[100];
int cot;


int judge(int i,int fro)
{
    return i==fro ? 1:-1;
}

ll dfs(int pos,int lim,int num,int fro)
{
    if(pos<0)
        return abs(64-num);
    if(!lim&&dp[pos][fro][num]!=-1)
        return dp[pos][fro][num];
    int ed = lim ? dig[pos] : 1;
    ll ans = 0 ;
    for(int i = 0 ; i<=ed ;i++)
    {
        if( fro== 2 && i==0)
            ans += dfs(pos-1,lim&(i==ed),num,2);
        else if( fro == 2 && i==1)
            ans += dfs(pos-1,lim&(i==ed),num,i);
        else
            ans += dfs(pos-1,lim&(i==ed),num+judge(i,fro),i);
        ans %= mod;
    }
    if( !lim)
        dp[pos][fro][num] = ans;
    return ans;
}
int main()
{


    ll n;
    int T;
    scanf("%d",&T);
    memset(dp,-1,sizeof(dp));
    while(T--)
    {
        memset(dig,0,sizeof(dig));
        scanf("%lld",&n);
        cot = 0;
        while(n)
        {
            dig[cot++] = n%2;
            n/=2;
        }

        printf("%lld\n",dfs(cot-1,1,64,2));
    }

    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值