學軍信友隊趣味網絡邀請賽 D-抗疫鬥爭

抗役鬥爭

時間限制:2000ms

空間限制:512MB

題面描述

新冠疫情爆發以來,病毒不斷地擴散傳播,而人類也在不斷採取各種措施遏制病毒傳播。於是我們可以爲這場抗疫鬥爭建立一個數學模型,將病毒的不斷傳播和人類的不斷採取措施抽象爲一場雙方輪流行動的博弈。我們認爲人類與病毒的每輪行動都可以選擇一個正整數作爲行動值來評估。然而,出於各方面限制,雙方的所有行動值總和必須等於一個數 ,且每次的行動值不能超過對方上輪的行動值。對人類來說,要遏制疫情,就應成爲最後行動的一方,也就是說,在本方的某次行動後,行動值總和mm恰好被消耗完。

假設人類先行動,那麼我們只需一鼓作氣消耗完所有mm點行動值,就能戰勝病毒。然而在最開始的階段出於認識不到疫情的嚴重性,往往最難開展大規模行動。出於這個原因,我們令hmh_m表示在行動值總和爲mm的情況下,人類(即先行動方)的第-次行動最少要多少行動值,才能保證自己必勝。

出於統計需要,某科學家記fi=nihmf_i=\sum_{n|i}h_m,並想知道i=1nfi\sum_{i=1}^{n}f_i。方便起見,對998244353取模。你能幫個忙嗎?

輸入格式

第一行輸入一個數n。

輸出格式

一行一個數,表示答案。

樣例輸入

3

樣例輸出

6

限制及約定

本題採用子任務形式評測

子任務編號 n\leq 分值
11 33 11
22 10001000 99
33 10510^5 3131
44 101110^{11} 2828
55 5×10135\times10^{13} 2626
66 101510^{15} 55

題解

首先根據題意推導hmh_m,可以發現以下結論:

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

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

否則當m%2==0m\%2==0hmh_m肯定不能爲奇數;

進一步推導可以得出hij=hihjh_{i*j}=h_i*h_j

經過利用上面結論對hmh_m打表發現hm=lowbit(m)h_m=lowbit(m),因此得出下面三檔寫法:

i=1nfi=i=1nmihm=m=1nhmi=1nm=m=1nhmnm\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

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

發現m=1nhmnm\sum_{m=1}^{n}h_m\lfloor\frac n m \rfloor,可以用整除分塊優化通過4檔,此時便需要計算一個i=1nlowbit(i)\sum_{i=1}^nlowbit(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);
}

此時你應該會發現,可以通過將lowbit(i)lowbit(i)分爲幾類,枚舉lowbit(i)lowbit(i)的值計算貢獻,公式如下:

P1P_1lowbit(i)=2klowbit(i)=2^k的貢獻,則可以得出p1=2K(n2K+n22K+n3K...)p_1=2^K*({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...}),因爲(n2K+n22K+n3K...)({\frac n {2^K}+\frac n {2*2^K}+\frac n {3^K}...})中會多計算一部分2k+12^{k+1}的值,所以下一次計算只需乘2k2^k便可以,所以可得下列公式:

g(n)=inig(n)=\sum_i\lfloor\frac n i\rfloor,則可得:

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

對於g(n)g(n)計算使用整除分塊可以通過5檔,如果使用n\sqrt 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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章