Top

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


late-x语法显示错误,请移步:https://blog.csdn.net/Anoy_acer/article/details/105333982

抗役斗争

时间限制:2000ms

空间限制:512MB

题面描述

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

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

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

输入格式

第一行输入一个数n。

输出格式

一行一个数,表示答案。

样例输入

1
3

样例输出

1
6

限制及约定

本题采用子任务形式评测

子任务编号 n$\leq$ 分值
$1$ $3$ $1$
$2$ $1000$ $9$
$3$ $10^5$ $31$
$4$ $10^{11}$ $28$
$5$ $5\times10^{13}$ $26$
$6$ $10^{15}$ $5$

题解

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

当$m=2$时$h_m=2$;

当$m\%2!=0$时$h_m=1$;

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

进一步推导可以得出$h_{ij}=h_ih_j$

经过利用上面结论对$h_m$打表发现$h_m=lowbit(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$

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)$的值计算贡献,公式如下:

设$P_1$为$lowbit(i)=2^k$的贡献,则可以得出$p_1=2^K({\frac n {2^K}+\frac n {22^K}+\frac n {3^K}…})$,因为$({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}…})$中会多计算一部分$2^{k+1}$的值,所以下一次计算只需乘$2^k$便可以,所以可得下列公式:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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);
}
文章版权为Anoyer博客所有,转载请以链接形式标明本文地址