【nkoj 5376】illustrious

题目大意

\(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();
}
posted @ 2019-01-15 01:47 蒟蒻小果冻 阅读(...) 评论(...)  编辑 收藏