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

抗役斗争

时间限制:2000ms

空间限制:512MB

题面描述

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

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

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

输入格式

第一行输入一个数n。

输出格式

一行一个数,表示答案。

样例输入
3
样例输出
6
限制及约定

本题采用子任务形式评测

子任务编号n ≤ \leq 分值
1 1 1 3 3 3 1 1 1
2 2 2 1000 1000 1000 9 9 9
3 3 3 1 0 5 10^5 105 31 31 31
4 4 4 1 0 11 10^{11} 1011 28 28 28
5 5 5 5 × 1 0 13 5\times10^{13} 5×1013 26 26 26
6 6 6 1 0 15 10^{15} 1015 5 5 5

题解

首先根据题意推导 h m h_m hm,可以发现以下结论:

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

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

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

进一步推导可以得出 h i ∗ j = h i ∗ h j h_{i*j}=h_i*h_j hij=hihj

经过利用上面结论对 h m h_m hm打表发现 h m = l o w b i t ( m ) h_m=lowbit(m) hm=lowbit(m),因此得出下面三档写法:

∑ i = 1 n f i = ∑ i = 1 n ∑ m ∣ i h m = ∑ m = 1 n h m ∑ i = 1 n m = ∑ m = 1 n h m ⌊ n m ⌋ \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 i=1nfi=i=1nmihm=m=1nhmi=1mn=m=1nhmmn

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

发现 ∑ m = 1 n h m ⌊ n m ⌋ \sum_{m=1}^{n}h_m\lfloor\frac n m \rfloor m=1nhmmn,可以用整除分块优化通过4档,此时便需要计算一个 ∑ i = 1 n l o w b i t ( i ) \sum_{i=1}^nlowbit(i) i=1nlowbit(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);
}

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

P 1 P_1 P1 l o w b i t ( i ) = 2 k lowbit(i)=2^k lowbit(i)=2k的贡献,则可以得出 p 1 = 2 K ∗ ( n 2 K + n 2 ∗ 2 K + n 3 K . . . ) p_1=2^K*({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...}) p1=2K(2Kn+22Kn+3Kn...),因为 ( n 2 K + n 2 ∗ 2 K + n 3 K . . . ) ({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...}) (2Kn+22Kn+3Kn...)中会多计算一部分 2 k + 1 2^{k+1} 2k+1的值,所以下一次计算只需乘 2 k 2^k 2k便可以,所以可得下列公式:

g ( n ) = ∑ i ⌊ n i ⌋ g(n)=\sum_i\lfloor\frac n i\rfloor g(n)=iin,则可得:

S ( n ) = g ( n ) + ∑ k = 1 2 k − 1 g ( ⌊ n 2 k ⌋ ) S(n)=g(n)+\sum_{k=1}2^{k-1}g(\lfloor\frac n {2^k}\rfloor) S(n)=g(n)+k=12k1g(2kn)

对于 g ( n ) g(n) g(n)计算使用整除分块可以通过5档,如果使用 n \sqrt n 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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值