【HDU - 6304】Chiaki Sequence Revisited

@Chiaki Sequence Revisited@


@题目描述-English@

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

an=1(n=1,2) a n = 1 ( n = 1 , 2 )

an=anan1+an1an2(n3) a n = a n − a n − 1 + a n − 1 − a n − 2 ( n ≥ 3 )

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

Input
There are multiple test cases. The first line of input contains an integer T (1≤T≤10^5), indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤10^18).
Output
For each test case, output an integer denoting the answer.

Sample Input
10
1
2
3
4
5
6
7
8
9
10
Sample Output
1
2
4
6
9
13
17
21
26
32

@大致题意@

数组 a 递推公式如上,求数组 a 的前缀和。

@分析 - 遇事不决先打表@

如题,数列 a 打表如下(`・ω・´):

a:1,1,2,2,3,4,4,4,5,6,6,7,8,8,8,8,9,10,10,11,12,12,12,13,14,14,15,16,16,16,16,16,…

目测好像有点儿规律的样子(`・ω・´)
记f[x]表示 x 连续出现多少次,继续来打波表(`・ω・´)

f:2,2,1,3,1,2,1,4,1,2,1,3,1,2,1,…

如果将f[1]减1,就可以发现 f 数组其实真的是有规律的(`・ω・´)
f 数组的前 1(2^1 - 1) 个:1
f 数组的前 3(2^2 - 1) 个:1,2,1
f 数组的前 7(2^3 - 1) 个:1,2,1,3,1,2,1
f 数组的前 15(2^4 - 1) 个:1,2,1,3,1,2,1,4,1,2,1,3,1,2,1
这样规律就非常明显了嘛(`・ω・´)

@分析 - 利用规律瞎求解@

首先考虑怎么表达“将f[1]减1”,其实这个是等价于“忽略a[1]”,我们可以将项数N-1,求解一遍前缀和,再让最后答案+1。

然后,记 s 为 f 数组的前缀和数组,我们找到数 x 使得 s[x] <= N 且 s[x+1] > N,则最后答案为 (xi=1if[i])+(Ns[x])(x+1) ( ∑ i = 1 x i ∗ f [ i ] ) + ( N − s [ x ] ) ∗ ( x + 1 )

考虑怎么找这样一个 x,因为 f 数组有着极好的自相似性,我们考虑用倍增法。记 cnt[i]=s[2i1] c n t [ i ] = s [ 2 i − 1 ] ,则有 cnt[i]=cnt[i1]2+i c n t [ i ] = c n t [ i − 1 ] ∗ 2 + i ,可以用类似于求 LCA 的方法求出 x :先找到最大的 p 满足 cnt[p] <= N,如果cnt[p] + (p+1) > N,则将 2^p - 1 累加进 x,跳出迭代;否则将 2^p 累加进 x ,将 N 减去 cnt[p] + (p+1) ,继续迭代操作。

再考虑怎么求解 xi=1if[i] ∑ i = 1 x i ∗ f [ i ] ,我们也可以利用 f 数组的自相似性来使用倍增法。
sum[x]=2x1i=1if[i] s u m [ x ] = ∑ i = 1 2 x − 1 i ∗ f [ i ]
因为 f[1...2x11]=f[2x1+1...2x1] f [ 1...2 x − 1 − 1 ] = f [ 2 x − 1 + 1...2 x − 1 ] (说白就是关于中间点对称)
sum[x]=(2x11i=1if[i])+(2x11i=1(i+2x1)f[i])+2x1x s u m [ x ] = ( ∑ i = 1 2 x − 1 − 1 i ∗ f [ i ] ) + ( ∑ i = 1 2 x − 1 − 1 ( i + 2 x − 1 ) ∗ f [ i ] ) + 2 x − 1 ∗ x
sum[x]=sum[x1]+2x1x+(sum[x1]+2x1cnt[x1]) s u m [ x ] = s u m [ x − 1 ] + 2 x − 1 ∗ x + ( s u m [ x − 1 ] + 2 x − 1 ∗ c n t [ x − 1 ] )
就可以对 sum 数组进行递推
其他的和上面的方法相类似,也是先找到一个最大且小于等于 x 的数,分两类讨论,详细细节见代码。

@代码@

【我一开始连这道题题面都不知道,队友给我一个规律我就照着规律写了233】
【万能的队友】

#include<cstdio>
#include<cmath>
typedef long long ll;
const int MOD = int(1E9) + 7;
ll cnt[64], sum[64], pw2[64];
ll cnt_mod[64], pw2_mod[64];
void init() {
    cnt[1] = 1; pw2[1] = 2; sum[1] = 1;
    cnt_mod[1] = 1, pw2_mod[1] = 2;
    for(int i=2;i<=60;i++) {
        cnt[i] = cnt[i-1]*2 + i;//这里不能取模!
        sum[i] = ((sum[i-1] + pw2_mod[i-1]*i%MOD)%MOD + (sum[i-1] + cnt_mod[i-1]*pw2_mod[i-1]%MOD)%MOD)%MOD;
        pw2[i] = pw2[i-1] * 2;//这里也不能取模!
        cnt_mod[i] = (cnt_mod[i-1]*2 + i) % MOD;
        pw2_mod[i] = pw2_mod[i-1] * 2 % MOD;
    } 
}
ll solve(ll N) {
    ll x = 0;
    for(int i=60;i>=1;i--) {
        if( cnt[i] <= N ) {
            if( cnt[i] + (i+1) > N ) {
                x += pw2[i] - 1;
                N -= cnt[i];
                break;
            }
            else {
                x += pw2[i];
                N -= (cnt[i] + (i + 1));
            }
        }
    }
    ll ret = 0, lst = 0, temp = x;
    for(int i=60;i>=1;i--) {
        if( pw2[i] - 1 <= temp ) {
            if( pw2[i] > temp ) {
                ret = (ret + sum[i] + cnt_mod[i]*lst%MOD)%MOD;
                break;
            }
            else {
                ret = (ret + sum[i] + cnt_mod[i]*lst%MOD)%MOD;
                ret = (ret + (pw2_mod[i]+lst)%MOD*(i+1)%MOD)%MOD;
                lst = (lst + pw2_mod[i]%MOD)%MOD;
                temp -= pw2[i];
            }
        }
    }
    return (ret + N%MOD*(x+1)%MOD)%MOD;
}
int main() {
    init(); int T;
    scanf("%d", &T);
    for(int i=1;i<=T;i++) {
        ll N;
        scanf("%lld", &N);
        printf("%lld\n", (solve(N-1) + 1) % MOD);
    }
}

@END@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值