欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学军信友队趣味网络邀请赛 D-抗疫斗争

程序员文章站 2024-03-18 23:26:22
...

抗役斗争

时间限制:2000ms

空间限制:512MB

题面描述

新冠疫情爆发以来,病毒不断地扩散传播,而人类也在不断采取各种措施遏制病毒传播。于是我们可以为这场抗疫斗争建立一个数学模型,将病毒的不断传播和人类的不断采取措施抽象为一场双方轮流行动的博弈。我们认为人类与病毒的每轮行动都可以选择一个正整数作为行动值来评估。然而,出于各方面限制,双方的所有行动值总和必须等于一个数 ,且每次的行动值不能超过对方上轮的行动值。对人类来说,要遏制疫情,就应成为最后行动的一方,也就是说,在本方的某次行动后,行动值总和mm恰好被消耗完。

假设人类先行动,那么我们只需一鼓作气消耗完所有mm点行动值,就能战胜病毒。然而在最开始的阶段出于认识不到疫情的严重性,往往最难开展大规模行动。出于这个原因,我们令hmh_m表示在行动值总和为mm的情况下,人类(即先行动方)的第-次行动最少要多少行动值,才能保证自己必胜。

出于统计需要,某科学家记fi=nihmf_i=\sum_{n|i}h_m,并想知道i=1nfi\sum_{i=1}^{n}f_i。方便起见,对998244353取模。你能帮个忙吗?

输入格式

第一行输入一个数n。

输出格式

一行一个数,表示答案。

样例输入

3

样例输出

6

限制及约定

本题采用子任务形式评测

子任务编号 n\leq 分值
11 33 11
22 10001000 99
33 10510^5 3131
44 101110^{11} 2828
55 5×10135\times10^{13} 2626
66 101510^{15} 55

题解

首先根据题意推导hmh_m,可以发现以下结论:

m=2m=2hm=2h_m=2

m%2!=0m\%2!=0hm=1h_m=1;

否则当m%2==0m\%2==0hmh_m肯定不能为奇数;

进一步推导可以得出hij=hihjh_{i*j}=h_i*h_j

经过利用上面结论对hmh_m打表发现hm=lowbit(m)h_m=lowbit(m),因此得出下面三档写法:

i=1nfi=i=1nmihm=m=1nhmi=1nm=m=1nhmnm\sum_{i=1}^{n}f_i=\sum_{i=1}^{n}\sum_{m|i}h_m=\sum_{m=1}^{n}h_m\sum_{i=1}^{\frac n m}=\sum_{m=1}^{n}h_m\lfloor\frac n m \rfloor

for(int i=1;i<=n;++i){
	ans=(ans+lowbit(i)*n/i%mod)%mod;
}

发现m=1nhmnm\sum_{m=1}^{n}h_m\lfloor\frac n m \rfloor,可以用整除分块优化通过4档,此时便需要计算一个i=1nlowbit(i)\sum_{i=1}^nlowbit(i),代码实现如下:

ll B[70];
ll sum(ll n){
    ll ans=0;
    for(int i=0;i<63;++i){
        if((1ll<<i)&n)ans=(ans+B[i])%mod;
    }
    return ans;
}
void solve(){
    for(int i=0;i<63;++i){
        for(int j=0;j<i;++j){
            B[i]=(B[i]+qpow(2,j,mod)*qpow(2,i-j-1,mod)%mod)%mod;
        }
        B[i]=(B[i]+qpow(2,i,mod))%mod;
    }
    ll n;
    sf(n);
    ll ans=0;
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=(ans+(sum(r)-sum(l-1)+mod)%mod*(n/l)%mod)%mod;
    }
    pf(ans);
}

此时你应该会发现,可以通过将lowbit(i)lowbit(i)分为几类,枚举lowbit(i)lowbit(i)的值计算贡献,公式如下:

P1P_1lowbit(i)=2klowbit(i)=2^k的贡献,则可以得出p1=2K(n2K+n22K+n3K...)p_1=2^K*({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...}),因为(n2K+n22K+n3K...)({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...})中会多计算一部分2k+12^{k+1}的值,所以下一次计算只需乘2k2^k便可以,所以可得下列公式:

g(n)=inig(n)=\sum_i\lfloor\frac n i\rfloor,则可得:

S(n)=g(n)+k=12k1g(n2k)S(n)=g(n)+\sum_{k=1}2^{k-1}g(\lfloor\frac n {2^k}\rfloor)

对于g(n)g(n)计算使用整除分块可以通过5档,如果使用n\sqrt n枚举将通过六档

int Mod(int a,int b){
    if(a+b>=mod)return a+b-mod;
    return a+b;
}
ll solve1(ll n){//整除分块写法
    int ans=0;
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=(ans+(r-l+1)*(n/l)%mod)%mod;
    }
    return ans;
}
int solve(ll n){    //sum_{i=1}(n/i)
    ll ans=0;
    int sq=sqrt(n);
    for(int i=1;i<=sq;++i)ans+=n/i;
    ans=2ll*ans-1ll*sq*sq;
    return ans%mod;
}
Anoyer(){
#ifdef LOCAL
    fcin;
    //fcout;
#endif
    ll n;
    sf(n);
    int ans=solve(n);
    ll tmp=1;
    for(ll i=2;i<=n;i<<=1){
        ans=Mod(ans,tmp*solve(n/i)%mod);tmp=i%mod;
    }
    pf(ans);
}