题目大意:
一个序列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; }