抗役鬥爭
時間限制:2000ms
空間限制:512MB
題面描述
新冠疫情爆發以來,病毒不斷地擴散傳播,而人類也在不斷採取各種措施遏制病毒傳播。於是我們可以爲這場抗疫鬥爭建立一個數學模型,將病毒的不斷傳播和人類的不斷採取措施抽象爲一場雙方輪流行動的博弈。我們認爲人類與病毒的每輪行動都可以選擇一個正整數作爲行動值來評估。然而,出於各方面限制,雙方的所有行動值總和必須等於一個數 ,且每次的行動值不能超過對方上輪的行動值。對人類來說,要遏制疫情,就應成爲最後行動的一方,也就是說,在本方的某次行動後,行動值總和恰好被消耗完。
假設人類先行動,那麼我們只需一鼓作氣消耗完所有點行動值,就能戰勝病毒。然而在最開始的階段出於認識不到疫情的嚴重性,往往最難開展大規模行動。出於這個原因,我們令表示在行動值總和爲的情況下,人類(即先行動方)的第-次行動最少要多少行動值,才能保證自己必勝。
出於統計需要,某科學家記,並想知道。方便起見,對998244353取模。你能幫個忙嗎?
輸入格式
第一行輸入一個數n。
輸出格式
一行一個數,表示答案。
樣例輸入
3
樣例輸出
6
限制及約定
本題採用子任務形式評測
子任務編號 | n | 分值 |
---|---|---|
題解
首先根據題意推導,可以發現以下結論:
當時;
當時;
否則當時肯定不能爲奇數;
進一步推導可以得出
經過利用上面結論對打表發現,因此得出下面三檔寫法:
for(int i=1;i<=n;++i){
ans=(ans+lowbit(i)*n/i%mod)%mod;
}
發現,可以用整除分塊優化通過4檔,此時便需要計算一個,代碼實現如下:
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);
}
此時你應該會發現,可以通過將分爲幾類,枚舉的值計算貢獻,公式如下:
設爲的貢獻,則可以得出,因爲中會多計算一部分的值,所以下一次計算只需乘便可以,所以可得下列公式:
設,則可得:
對於計算使用整除分塊可以通過5檔,如果使用枚舉將通過六檔
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);
}