题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=5399
题解
打表观察 f f 序列,我们发现就是 i i 这个数在这个序列中出现的次数。
因此,如果我们暴力算出 f f 的前项的值,我们就可以求出 f f 的前项的值。
而 g g 序列是的前缀和,那么 g(n) g ( n ) 的含义就是 f(x)=n f ( x ) = n 的最大的 x x 。
考虑中每一项的含义。
f(f(n)) f ( f ( n ) ) 就是 f(n) f ( n ) 的出现次数。
g(f(n)) g ( f ( n ) ) 就是序列中最大为 f(n) f ( n ) 的位置。
那么 g(f(n))−f(f(n)) g ( f ( n ) ) − f ( f ( n ) ) 就是序列中最大的等于 f(n)−1 f ( n ) − 1 的位置,即 g(f(n)−1) g ( f ( n ) − 1 ) 。
因此 h(n) h ( n ) 就是 n n 位置之前的每一段数的结尾位置的之和。
打表发现
g(g(n))=∑ni=1i×f(i)
g
(
g
(
n
)
)
=
∑
i
=
1
n
i
×
f
(
i
)
,可以边算
h
h
<script type="math/tex" id="MathJax-Element-26">h</script>边算这个。
代码
#include <cstdio>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxm=1000000;
const int mo=998244353;
const int half=(mo+1)>>1;
int f[maxm+10];
int main()
{
f[1]=1;
for(int i=2; i<=maxm; ++i)
{
f[i]=f[i-f[f[i-1]]]+1;
}
int t=read();
while(t--)
{
int n=read(),tot=0,g=0,i=1,res=0;
while(tot+f[i]<=n)
{
int tmp=(2ll*tot+f[i]+1)%mo*f[i]%mo*half%mo;
g=(g+1ll*tmp*i)%mo;
res=(res+g)%mo;
tot+=f[i++];
}
if(tot<n)
{
int tmp=(1ll*tot+n+1)%mo*(n-tot)%mo*half%mo;
g=(g+1ll*tmp*i)%mo;
res=(res+g)%mo;
}
printf("%d\n",res);
}
return 0;
}