[bzoj5399]illustrious——打表找规律

题面:

这里写图片描述

思路:

好一个打表题。。。
首先我们要发现各种性质:
f[n] f [ n ] 直接对应了 n n 在序列中出现的次数,又因为f[n]是单调递增的,然后我们就可以通过前 1e6 1 e 6 的数据二分来算出后面任意一个 f[n] f [ n ]
至于怎么算 g[g[n]] g [ g [ n ] ] 发现它的差分数组为 nf[n] n ∗ f [ n ] ,所以 g[g[n]]=ni=1if[i] g [ g [ n ] ] = ∑ i = 1 n i ∗ f [ i ] ,因为中间的 f[i] f [ i ] 不同的数只有 1e6 1 e 6 个,相同的 f[i] f [ i ] 可以通过等差数列求和来算,计算出和的前缀和,然后再二分一下算末尾的片段就好了。
然后就可以化简 h[n]=h[g[f[n]1]]+g[g[n]] h [ n ] = h [ g [ f [ n ] − 1 ] ] + g [ g [ n ] ] 。理论上来说这样的话这题就可以过了,但是带了一个 log log 也是美中不足,
观察到 g[n] g [ n ] 的意义为 f[n] f [ n ] 的前缀和,也就是 f[m]=n f [ m ] = n 的m的最大值,也就是一段相同的数的下标的最后一个,所以我们可以一整块一整块地从前往后推,同时计算 g[g[n]] g [ g [ n ] ] 的时候也可以利用前面的状态了。
这题教给我太多了。。。。

/*======================================
 * Author : ylsoi
 * Problem : illustrious
 * Algorithm : print the table and math
 * Time : -2018.6.22
 * =====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("20180622T3.in","r",stdin);
    freopen("20180622T3.out","w",stdout);
}

const int maxn=1e6+10;
const ll mod=998244353;
int T,n;
ll f[maxn],g[maxn],gsum[maxn],gg[maxn];

ll F(ll x){return lower_bound(g+1,g+maxn-9,x)-g;}

ll GG(ll x){
    int pos=upper_bound(g+1,g+maxn-9,x)-g-1;
    return (gsum[pos]+(pos+1)*(g[pos]+1+x)*(x-g[pos])/2)%mod;
}

void init(){
    f[1]=1;
    REP(i,2,maxn-10)f[i]=f[i-f[f[i-1]]]+1;
    REP(i,1,maxn-10)g[i]=g[i-1]+f[i];
    REP(i,1,maxn-10)gsum[i]=(gsum[i-1]+i*(2*g[i-1]+f[i]+1)*f[i]/2)%mod;
    REP(i,1,1000)gg[i]=g[g[i]];
}

ll cal(ll x){
    ll ret=0;
    while(x){
        ret=(ret+GG(x))%mod;
        ll nex=g[F(x)-1];
        x=nex;
    }
    return ret;
}

int main(){
    File();
    init();
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        printf("%lld\n",cal(n));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值