2018杭电多校day1_G - Chiaki Sequence Revisited HDU - 6308

版权声明:随便写写,也不重要,欢迎挑错讨论 https://blog.csdn.net/qq_37305947/article/details/82467502

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

an={1an−an−1+an−1−an−2n=1,2n≥3an={1n=1,2an−an−1+an−1−an−2n≥3


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

Input

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

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了,并没有仔细的去想去写。然后时隔大半个月后返回来想补这个题的题解,发现,这个题并不是想象中那么简单。。。

首先题意很清楚,求数列前缀和。

思路也很清楚,一般这种题都是打表找规律。

随随便便打一个100的表。

大致是这个样子的1,1,2,2,3,4,4,4,5,6,6,7,8,8,8,8.....

这么瞎看肯定看不出来什么蛇皮,抛掉第一个1按每个数字出现的次数重新整理一下数列

1,

2,2

3,

4,4,4

5,

6,6

7,

8,8,8,8,

9,

10,10

开心的发现:

1,3,5,7,9.....(2*t-1) 每个数字只出现一次

2,6,10,14,18.....(4*t-2) 每个数字都出现两次

4,12,20,28,36.......(8*t-4) 每个数字都出现三次

(t=0,1,2,3,.......)

每一行都是等差数列 :

且令每一个数列首项为  2^t 则公差为 2 ^ ( t+1) 

所以每一行 都等差求和(首项加末项乘项数除以2)然后再乘对应出现的次数

附每一行每个数字出现的次数与__builtin_ctz(unsigned int n) 这个函数有关(这个函数不认识,也不知道,直播里说到的,记一下)传送门

然后 高潮来了 接下来的计算就是要计算每一行等差数列 然后再乘就可以,求和公式很简单,但是要确定每一项就很麻烦。。

代码里的做法是:

  用两个数组分别存每一行首项的大小:即2^(n-1)  和每一行首项最后出现的位置 即2^n-1

        eg:第一行首项最后出现的位置是1

                第二行首项最后出现的位置是3

                第三行首项最后出现的位置是7

                第四行首项最后出现的位置是15

  然后从头开始减个数 由此判断最后剩下几个数,同时把能减掉的数都加起来来判断每一行都有多少个数。

在求每一行等差求和 乘相关系数 叠加 最后加上 剩下的数*剩下的数的个数

。。。。。。。感觉解释的一塌糊涂。。。。

代码附上自己理解吧。。。。

#include<bits/stdc++.h>
#define CaseT 	int t;cin>>t;while(t--) 
#define ms(a,x) memset(a,x,sizeof(a))
using namespace std;

#define N 200
#define MAX 100002

typedef long long ll;
const int maxt=1e8;
const int maxn=1e8+5;
const int mod=1e9+7;
ll Num[N],p[N];
void init(){
	Num[0]=1;
	p[0]=1;
	for(int i=1;i<=63;i++){
		Num[i]=Num[i-1]*2+1;
		p[i]=p[i-1]*2;
	}
}
ll n;
int inv2=(mod+1)/2;
ll getsum(ll pos){
	ll sum=0;
	for(ll i=1;i<=pos;i*=2){
		ll num=(pos-i)/(2*i);
		ll Max=i+num*(2*i);//末项
			((Max+i)%mod*num)%mod ;
		num = (num+1)%mod;//项数 
		Max = (Max+i)%mod;
		Max = (Max*num)%mod;
		Max = (Max*inv2)%mod;
		
		Max = Max*(__builtin_ctz(i)+1)%mod;
		
		sum = (sum+Max)%mod;
	}
	return (sum+1)%mod;
} 
int solve(){
	init();
	int t;
	cin>>t;
	while(t--){
		scanf("%lld",&n);
		ll temp,pos=0;
		n--;
		if(!n) {
			printf("1\n");
			continue;
		}
		temp =  n ;
		for(int i=62;i>=0;i--){
			if(temp >= Num[i]){
				
				temp-=Num[i];
				pos+=p[i]; 
			}
		}
		ll sum = getsum(pos);
		if(temp){
			sum = (sum + temp%mod * (pos+1)%mod )%mod;
		} 
		cout<<sum<<endl;
	}
	
}
int main(){
	solve();
	return 0;
}

 网上还有好多解释很详细的博客,一并贴出来供大家一起学习~ 

感觉讲解清晰,代码不清晰

神奇公式

猜你喜欢

转载自blog.csdn.net/qq_37305947/article/details/82467502
今日推荐