学军信友队趣味网络邀请赛 D.抗疫斗争
D. 抗疫斗争
时间限制:\(2000ms\)
空间限制:\(512MB\)
题目描述
新冠疫情爆发以来,病毒不断地扩散传播,而人类也在不断采取各种措施遏制病毒传播。于是我们可以为这场抗疫斗争建立一个数学模型,将病毒的不断传播和人类的不断采取措施抽象为一场双方轮流行动的博弈。我们认为人类与病毒的每轮行动都可以选择一个正整数作为行动值来评估。然而,出于各方面限制,双方的所有行动值总和必须等于一个数\(m\),且每次的行动值不能超过对方上轮的行动值。对人类来说,要遏制疫情,就应成为最后行动的一方,也就是说,在本方的某次行动后,行动值总和\(m\)恰好被消耗完。
假设人类先行动,那么我们只需一鼓作气消耗完所有 \(m\)点行动值,就能战胜病毒。然而在最开始的阶段出于认识不到疫情的严重性,往往最难开展大规模行动。出于这个原因,我们令\(h_m\)表示在行动值总和为\(m\)的情况下,人类(即先行动方)的第一次行动最少要多少行动值,才能保证自己必胜。
出于统计需要,某科学家记\(f_i=\sum_{m|i}h_m\),并想知道\(\sum_{i=1}^n f_i\)。方便起见,对\(998244353\)取模。你能帮个忙吗?
输入格式
第一行输入一个数\(n\)。
输出格式
一行一个数,表示答案。
样例输入
\(3\)
样例输出
\(6\)
限制及约定
本题采用子任务形式评测。
子任务编号 | \(n<=\) | 分值 |
---|---|---|
\(1\) | \(3\) | \(1\) |
\(2\) | \(1000\) | \(9\) |
\(3\) | \(10^5\) | \(31\) |
\(4\) | \(10^{11}\) | \(28\) |
\(5\) | \(5*10^{13}\) | \(26\) |
\(6\) | \(10^{15}\) | \(5\) |
对于所有数据,满足\(1<=n<=10^{15}\)。 |
思路
我们可以算出规律\(h_m=lowbit(m)\)
\(S(n)=\sum_{i=1}^n\sum_{m|i}h_m=\sum_{m=1}^nh_m\lfloor\frac{n}{m}\rfloor\)
之后我们就可以计算\(h_m\)的前\(n\)项和然后分块去做,但这样只能通过到任务4
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
LL work(LL n)
{
if(n==1||n==0)return n;
LL ans=2*work(n>>1)+n/2;
if(n%2)ans++;
return ans%mod;
}
int main()
{
LL n;
scanf("%lld",&n);
LL ans=0;
for(LL l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=(ans+n/l*(work(r)-work(l-1))%mod)%mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}
我们考虑枚举\(h_m\)的取值,并统一计算其贡献。
\(h_m=lowbit(m)=2^k\)的贡献为:\(2^k(\frac{n}{2^k}+\frac{n}{2*2^k}+\frac{n}{3*2^k}+...)\),因为这部分多算了后面\(2^{k+1},2^{k+2},...\)的贡献,所以后面部分算的时候要减掉已经算过的,所以只需乘以\((2^k-2^{k-1})\)
比如:
因为\(1,3,5,7...\)这样不好算
所以我们可以这样去算
但这样的话后面的就在这里多算了\(2^0\)次,所以后面乘的值就要减掉前面的。
这里也多算了后面的,所以算后面时也要减掉
......
所以最后就等于
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
LL g(LL n)
{
/* //95分,最后一个任务过不了
LL res=0;
for(LL l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
res=(res+(r-l+1)%mod*(n/l)%mod)%mod;
}
return res;*/
LL res=0;
LL p=sqrt(n);
for(LL i=1;i<=p;i++)res=(res+n/i)%mod;
return (2*res-p*p)%mod;
}
int main()
{
LL n;
scanf("%lld",&n);
LL ans=g(n);
for(LL i=1;(1LL<<i)<=n;i++)
ans=(ans+(1LL<<(i-1))%mod*g(n/(1LL<<i)))%mod;
printf("%lld\n",ans);
return 0;
}