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;
}