题目大意
令\(f[n]=f[n-f[f[n-1]]]+1\),
\(g[n]=\sum_{i=1}^{n}f[i]\),
\(h[n]=h\){\(g[f(n)]-f[f(n)]\)}\(+g[g[n]]\)
求\(h[n](n\leq 10^9)\)
题目来源
HNOI2018省队集训
题解
极为神仙的一道题。
首先打表出f 序列,可以发现\(f[i]\) 就是序列中 i 出现的次数(当然也可以证明,但谁会去那么做呢?)
然后考虑g的含义,\(g[i]\)为 f 序列中1出现的次数+2出现的次数+……+i出现的次数。因为序列单调不降,故\(g[i]\)就是f序列中最后一个小于等于 i的数的位置,也是最后一个值为i 的数的位置。
考虑\(g[f(n)]−f[f(n)]\)的含义。\(g[f(n)]\)为最后一个值为\(f[n]\)的数的位置,\(f[f(n)]\)为\(f[n]\)出现次数。作差后就成了最后一个值为\((f[n]-1)\)的数的位置,即\(g[f(n)-1]\)。
那\(g[g[n]]\)呢?相邻两项作差,有
\(g[g[n]]-g[g[n-1]]=\sum_{i=g[n-1]+1}^{g[n]}f[i]\)
因为g的含义,所以此区间的f值均为n。故原式就为\(f[n]*n\)
因为作差,所以\(g[g[n]]=\sum_{i=1}^{n}f[i]*i\)
注意到除了第一次外,每次\(g[f(n)]−f[f(n)]\)都是值为某一个数的最后位置。于是从小到大枚举最后位置,更新此时的\(g[g[n]]\),再算答案即可
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int f[1000005];
const int MOD=998244353;
inline int add(int a,int b)
{a+=b;return a>=MOD?a-MOD:a;}
inline int mul(int a,int b)
{return 1LL*a*b%MOD;}
int i2=(MOD+1)/2;
void solve()
{
int n;
int als=0,tmp=0;
cin>>n;
for(int i=1,tot=0;;i++){
//(首项+末项)*项数/2
int en=min(tot+f[i],n);
int del=mul(mul(add(tot+1,en),en-tot),i2);
tmp=add(tmp,mul(del,i));
als=add(als,tmp);
if(tot+f[i]>=n){
cout<<als<<"\n";
return;
}
tot+=f[i];
}
}
int main()
{
f[1]=1;
for(int i=2;i<=1000000;i++)
f[i]=1+f[i-f[f[i-1]]];
int t;
for(cin>>t;t;--t)
solve();
}