题目链接:传送门
题意:
根据题目的意思构造一个数列,然后设f(x)为x最后一次出现的位置,求f(f(x)).
分析:
我是找规律得出的结果。
val: 1 2 2 3 3 4 4 4 5 5 5
index: 1 2 3 4 5 6 7 8 9 10 11
val[i] = val[i]=val[i-val[val[i-1]]]+1;
写了记住之后发现 f(f(x)) = sigma(val[i]*index[i]) 1<=i<=n;然后求和的时候将原来的数列按照val[i]相同的进行分组就好了。
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define IFOR(i,s,e) for(int i=s;i<e;i++)
using namespace std;
typedef long long LL;
const int maxn = 5e5;
const LL mod =1e9+7;
LL a[maxn];
LL sum[maxn];
LL tot[maxn];
LL quick_mod(LL a,LL b){
LL ans = 1;
while(b){
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
const LL inv = quick_mod(2LL,mod-2);
void init(){
a[1]=1;
sum[0]=0;
sum[1]=1;
IFOR(i,2,maxn){
a[i]=a[i-a[a[i-1]]]+1;
sum[i]=sum[i-1]+a[i];
}
tot[0]=0;
IFOR(i,1,maxn){
tot[i]=tot[i-1];
LL tmp = (sum[i-1]+1+sum[i])*a[i]%mod*(LL)i%mod*inv%mod;
tot[i]=(tot[i]+tmp)%mod;
}
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--){
LL n;
scanf("%I64d",&n);
int pos = lower_bound(sum,sum+maxn,n)-sum;
if(sum[pos]==n) printf("%I64d\n",tot[pos]);
else{
pos--;
LL num = tot[pos];
num = (num+(pos+1)*(n+sum[pos]+1)%mod*(n-sum[pos])%mod*inv%mod)%mod;
printf("%I64d\n",num);
}
}
return 0;
}