2019ICPC(银川) - Function!(数论+数学分块)
題目鏈接:點擊查看
題目大意:給出公式,其中,則,現在給出n(<=1e12),求出答案對998244353取模后的答案
題目分析:若用暴力實現上述公式,只需要兩層for循環,時間復雜度為n*n,也就是1e24。。所以我們需要想辦法優化,其實上面的公式看著嚇人,我們稍微化簡一下:
這樣看起來就親切多了,再其實仔細觀察一下就會發現,因為第二層循環的b是從a開始的,所以一定是大于零并且小于等于1,這樣向上取整就變為1了,乘上1就可以直接約分了:
這樣一來這個公式就比較好想了,因為n給的是1e12,這個數字很容易讓人聯想到sqrt(n),也就是1e6,所以我們不妨分一下塊,對第一層的a簡單分為[1,sqrt(n)]+[sqrt(n)+1,n],因為上面公式的b最大為1e12,所以只要a大于1e6,那么向下取整答案就是1了,也就是說對于[sqrt(n)+1,n]這一部分,我們可以稍微化簡一下:
現在利用等差數列求和公式以及平方和求和公式,就可以O(1)計算出區間[sqrt(n)+1,n]之間的答案了
而對于區間[1,sqrt(n)]中的答案,我們可以直接O(n)跑了,對于其中第二層循環b,雖然看著是要從a跑到1e12,但其實稍微思考一下就能發現,因為是要求的值作為答案,而這個值向下取整后,在a確定的情況下,的值在一段連續的區間上肯定是相同的,根據這個性質我們可以將1e12的數據分塊,分為一段一段的,每一段上的答案都為一個值,這樣分塊后我們就能很快的算出答案了,這個分塊最大是當a以2為底時,也就才40最大了,時間復雜度可以大膽地放縮,因為最大也就才1e6*40,5e7左右,在評測機上的表現還是十分優秀的
話說回來,我們該怎么求這個區間呢,其實稍微將上面的式子轉換一下:
等價于
所以我們可以從1開始枚舉x,從而計算出b的區間在內的都等于x,既然都是一個常數了,那么我們直接根據公式就能直接計算貢獻了,前面的公式也就是a*x*區間長度
最后需要注意一下關于取模時的一個巨坑,也就是遇到n就要模一下,不然1e12*1e9直接就把longlong爆掉了,還有就是在計算的過程中可能會出現負數,所以最后需要先模再加模最后再取模,基操基操
還有就是在計算等差數列求和公式和平方和求和公式時,會遇到除法,這個時候直接用費馬小定理求一下逆元就可以解決了
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e3+100;const int mod=998244353;LL inv2,inv6;//2的逆元和6的逆元LL q_pow(LL a,LL b) {LL ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans; }LL get_ans1(LL n)//1~a的等差和 {return (n%mod)*((n+1)%mod)%mod*inv2%mod; }LL get_ans2(LL n)//1~a*a的平方和 {return (n%mod)*((n+1)%mod)%mod*(2*n%mod+1)%mod*inv6%mod; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);inv2=q_pow(2,mod-2);inv6=q_pow(6,mod-2);LL n;cin>>n;LL t=sqrt(n);LL ans=(((n+1)%mod)*(get_ans1(n)-get_ans1(t))%mod-get_ans2(n)+get_ans2(t))%mod;for(LL a=2;a<=t;a++){LL x=1;//記錄loga(b)的值,即b=a^xLL b=a;LL sum=0;while(b<=n){sum=(sum+(min(b*a-1,n)-b+1)%mod*x%mod)%mod;b*=a;x++;}ans=(ans+sum*a%mod)%mod;}cout<<(ans%mod+mod)%mod<<endl;return 0; }?
總結
以上是生活随笔為你收集整理的2019ICPC(银川) - Function!(数论+数学分块)的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 2019ICPC(银川) - Large
 - 下一篇: CodeForces - 86D Pow