1. 程式人生 > >HDU 6069 數論 區間素數篩(+賽後反思

HDU 6069 數論 區間素數篩(+賽後反思

題目連結

x=pa11pa22....pann
d(x)=(1+a1)(1+a2)...(1+an)
d(xk)=(1+ka1)(1+ka2)(1+ka3)...(1+kan)

k已知,所以我們只需要知道L<=x<=R的每一個x質因數分解後的a1,a2...an便可以求解出答案。

x<=1e12,故可以考慮枚舉出1e6以內的素數,採用區間素數篩求出每一個數的質因數指數。(可參考白書(《挑戰程式設計競賽》)素數一章的例題)

設區間長度為n
總複雜度O(R+nloglogn)

程式碼:

#include <iostream>
#include <cstdio>
#include <cstdlib> #include <cmath> #include <vector> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int P = 998244353; const int G = 3; const int A = 1e6 + 10; const int N = 70; bool vis[A]; int pri[A],tot; void init(){ tot = 0
; for(ll i=2 ;i<A ;i++){ if(!vis[i]){pri[++tot] = i;} for(ll j=1 ;j<=tot && i*pri[j]<A ;j++){ vis[i*pri[j]] = 1; if(i%pri[j] == 0) break; } } } ll ans[A],val[A]; void solve(ll L,ll R,ll t){ for(ll i=L ;i<=R ;i++){ ans[i-L] = 1
; val[i-L] = i; } for(ll i=1 ;i<=tot ;i++){ ll now = pri[i]; for(ll j=max(2LL,(L+now-1)/now)*now ;j<=R ;j+=now){ if(val[j-L] % now) continue; ll cnt = 0; while(val[j-L] % now == 0){val[j-L]/=now;cnt++;} ans[j-L] = (1+t*cnt)%P*ans[j-L]%P; } } for(ll i=L ;i<=R ;i++){ if(val[i-L] > 1){ ans[i-L] = (1+t)%P*ans[i-L]%P; } } ll sum = 0; for(ll i=L ;i<=R ;i++){ sum = (sum + ans[i-L]) % P; } printf("%I64d\n",sum); } int main(){ init(); int T; scanf("%d",&T); while(T--){ ll l,r,k; scanf("%I64d%I64d%I64d",&l,&r,&k); solve(l,r,k); } return 0; }

反思:非常簡單的一題。本來我隊開場三十分鐘就推出了該公式並在白書上找到了區間篩的原題和程式碼。按正常劇情本應該是最多一小時內就能正確AC此題,但作為隊內此題的負責人,卻終場也沒有搞出來qwq
問題主要出在兩個方面。一是對自己的第一直覺太自信,賽前兩天才開始學FFT和NTT,一看到取模的質數是一個費馬質數,存在原根,雖然NTT學得並不好,但仍然固執地認定此題一定需要用到NTT進行優化,並帶偏了隊友的思路,在沒有跟隊友討論確定思路(因對需要使用NTT太自信,而兩位隊友都沒學過該演算法,故錯誤地認為沒有討論的必要)的情況下直接開寫,用NTT將

d(ik)=(1+ka1)(1+ka2)(1+ka3)...(1+kan)關於k的多項式進行展開運算,從而引起時間複雜度爆炸,提交的程式碼T了很多次,浪費了大量時間。

另外一方面便在於對演算法時間複雜度的估算不夠重視。本來半小時想出的思路是可以正確AC的,卻憑自我感覺認為其複雜度太高(因結果表示式太直接地就推了出來),跳過了複雜度估算的步驟,一直錯誤地思考著優化式子的辦法,並強行用上了NTT(果然還是太弱了qwq

認真反思一下,希望接下來的幾場能有更好的發揮吧qwq