BZOJ 5399 illustrious

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=5399

题解

打表观察 f f 序列,我们发现f(i)就是 i i 这个数在f这个序列中出现的次数。

因此,如果我们暴力算出 f f 的前106项的值,我们就可以求出 f f 的前3×109项的值。

g g 序列是f的前缀和,那么 g(n) g ( n ) 的含义就是 f(x)=n f ( x ) = n 的最大的 x x

考虑h(n)=h(g(f(n))f(f(n)))+g(g(n))中每一项的含义。

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))之和。

打表发现 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值