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 100002typedef 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;
}
网上还有好多解释很详细的博客,一并贴出来供大家一起学习~
感觉讲解清晰,代码不清晰
神奇公式