题目大意:
一个序列1、2、2、3、3、4、4、4、5、5、5……
第i项的数a[i]表示连续的i的个数,依次下去。
现给出n(n<=1e9),记最后出现n的位置为last[n],求last[last[n]]。
规律:last[last[n]]=a[1]+a[2]+……+a[last[n]]。比如last[3]=5,则last[last[3]]为序列的前5项的和,为11
知道了这个还不能算,因为n太大了。
再对上式进行归纳整理,可以得到:
last[last[n]]=1*1+(2+3)*2+(4+5)*3+……+(k+k+1+k+2+……+a[last[p-1]])*(p-1)+(a[last[p- 1]]+1+……+n)*p
其中每一个括号里面的数字构成等差序列,第i个括号里面的项数恰好是a[i](最后一个括号除外,最多只加到n),最后一项为a[last[i]]。
n最大可达到1e9,而p的最大值相对而言会小得多。
通过测试可以知道最大的p不会超过500000(事实上只有43万多一点)
那么,可以预处理出500000以内的序列a[i],last[i]。
以及前缀和ans[i]。
这里的ans[i]=1*1+(2+3)*2+(4+5)*3+……+(k+k+1+k+2+……+a[last[i])*i
对于每一个n,通过二分查找,找到p(其前面的括号项数刚好是a[i]的最大的p),即可马上得出答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define maxn 500000
#define mod 1000000007
typedef __int64 LL;
int a[maxn],last[maxn],tot;
LL ans[maxn];
void init()
{
int i;
a[1]=last[1]=1;
a[2]=2;
last[2]=3;
a[3]=2;
LL sum=3;
for(i=3;sum<1000000000;++i){
sum+=a[i];
last[i]=sum;
for(LL j=sum-a[i]+1;j<=min(sum,(LL)maxn);++j) a[j]=i;
}
tot=i-1;
ans[0]=0;
for(i=1;i<maxn;++i)
ans[i]=(ans[i-1]+(LL(i)*(last[i]-a[i]+1+last[i])%mod)*a[i]%mod*500000004%mod)%mod;
}
int Find(int n)
{
int l=1,r=tot,ans;
while(l<=r)
{
int m=(l+r)>>1;
if(last[m]<=n) {ans=m;l=m+1;}
else r=m-1;
}
return ans;
}
int main()
{
init();
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int p=Find(n);
LL res=ans[p];
if(last[p]<n) res=(res+LL(p+1)*(last[p]+1+n)%mod*(n-last[p])%mod*500000004)%mod;
printf("%I64d\n",res);
}
return 0;
}