紀中OJ 01.25【NOIP提高組】模擬 B 組 T2 數字對
阿新 • • 發佈:2019-02-10
logs alt 前綴 rip 一個 display .html rmq func
她的面前浮現出一個長度為 n 的序列 {ai},她想找出一段區間 [L, R] (1 <= L <= R <= n)。
這個特殊區間滿足,存在一個 k (L <= k <= R),並且對於任意的 i (L <= i <= R),ai 都能被 ak 整除。這樣的一個特殊區間 [L, R] 價值為 R - L。
小H想知道序列中所有特殊區間的 最大價值 是多少,而有多少個這樣的區間呢?這些區間又分別是哪些呢?你能幫助她吧。
第二行,n 個整數,代表 ai.
第二行 num 個整數,按升序輸出每個價值最大的特殊區間的 L.
Sample Input 輸入1: 5 4 6 9 3 6 輸入2: 5 2 3 5 7 11
Sample Output
輸出1:
1 3
2
輸出2:
5 0
1 2 3 4 5
60%: 1 <= n <= 3000 , 1 <= ai <= 1024.
80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.
100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.
分析:
看到了最大價值,即求最優解,腦海中立馬想到了:貪心、dp、優化的搜索(或暴力)和 二分答案 。顯然,此題是在序列中操作,數據範圍大,貪心條件不滿足,明顯的二分答案!!!
於是用二分答案求出序列的最大長度,但如何判斷答案 mid 的可行性和計算序列個數與開頭位置呢? 這樣,問題就轉化成如何判斷某個區間是否存在:對於任意的 i (L <= i <= R),ai 都能被 ak 整除 了。
顯然,搜索是最普遍的選擇,那這裏我就講講搜索的優化方案: 或許你們會問:不僅要詢問每個區間(O(n)),又要找出其中有沒有 ak 存在(O( n2)),時間復雜度不是 n3 嗎,怎麽過???
優化方案: 優化一: 對於任意的 i (L <= i <= R),ai 都能被 ak 整除 中的 ak,是很好求出來的,就是區間幾個數的最大公約數(gcd),在詢問前預處理一下,線上調用即可。
但是,對於一般的預處理(除前綴和和其他玄學預處理外),復雜度都是O(n2)的,一樣過不了(哭笑不得~)。。。
但是,在序列的維護中,自然想到 線段樹、樹狀數組、rmq 啦~(對於前兩項此題應該也能維護 gcd 吧?!~) 我們了解過的 rmq 都是維護最大最小值的,以其速度快,範圍廣而出名。此題,一樣可以維護區間的 gcd 。 舉個栗子: 4 6 9 若已求出前兩個數的 gcd 為2(rmq[1,1]=2),後一個數的 gcd 為 9(rmq[3,0]=a[3]=9),那區間的 gcd 就是 gcd(2,9)=1 啦~ 預處理時間復雜度降到 O(n log2 n)啦,在搜索時直接 O(1)的復雜度就能算出區間最小公約數(和 rmq 求最大最小值一樣的,換成 gcd 了而已)。 優化二:
通過優化一算出區間的 ak 後,我們就要開始找區間內有沒有這個 ak 了~ 然而我們發現,每個區間的搜索 O(n)不可省,而找 ak 又要 O(n),那豈不是 O(n2)嗎(雖然達不到,但也接近了,加上預處理和二分答案等,絕對過不了) 於是進一步優化: 畢竟我們只找一個數 ak,很容易想到出現一個數就在 flag 數組打個標記,找有沒有 ak 時直接判斷 flag[ak] 是否打過標記就行了。 而區間的移動一次的話,就 O(1)把新的那個數打一個標記,把出區間的那個刪掉標記即可~ 此過程時間復雜度將至 O(1),效率極高,是在某個區間搜某些數時的常用方法,可以經常拿來用。 優化三: 光有優化二是不行的,因為它用空間換時間,會 MLE 的。(畢竟 ak 可達 20 多億,數組開得了那麽多嗎???) 所以,在用了優化二後,常常用這種方法解決空間問題:哈希表~(它倆基本綁定了,哈希是利用無用的空間存有用的數。不會的自己學,不必多說) 但是,這裏的哈希有三種模式: 1. 查詢某數在哈希表中的位置(用於刪標記); 2. 把某數存進哈希表(用於打標記); 3. 查詢某數是否存在(用於查詢區間內是否有 ak)。 於是,我們用哈希表的稀少時間換了大量的空間~~~
所以總體說,預處理 O(n log2 n),二分 O(log2 n*check),在 check 中,區間詢問 O(n),找 ak O(hash)(hash 是哈希的時間復雜度,常數級別) 這樣,我們就把 O(n3)的時間復雜度將至 O(n log2 n+n log2 n*hash),粗略說就是 O(n log2 n),只是有點常數而已,不用卡常都能過(畢竟 2 秒時限)~~~ 下附標程,我就不寫註釋了~
聲明
Time Limits: 2000 ms Memory Limits: 262144 KB
Description
小 H 是個善於思考的學生,現在她又在思考一個有關序列的問題。她的面前浮現出一個長度為 n 的序列 {ai},她想找出一段區間 [L, R] (1 <= L <= R <= n)。
這個特殊區間滿足,存在一個 k (L <= k <= R),並且對於任意的 i (L <= i <= R),ai 都能被 ak 整除。這樣的一個特殊區間 [L, R] 價值為 R - L。
小H想知道序列中所有特殊區間的 最大價值
Input
第一行,一個整數 n.第二行,n 個整數,代表 ai.
Output
第一行兩個整數,num 和 val,表示價值最大的特殊區間的個數以及最大價值。第二行 num 個整數,按升序輸出每個價值最大的特殊區間的 L.
Sample Input 輸入1: 5 4 6 9 3 6 輸入2: 5 2 3 5 7 11
Sample Output
Data Constraint
30%: 1 <= n <= 30 , 1 <= ai <= 32.60%: 1 <= n <= 3000 , 1 <= ai <= 1024.
80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.
100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.
分析:
於是用二分答案求出序列的最大長度,但如何判斷答案 mid 的可行性和計算序列個數與開頭位置呢? 這樣,問題就轉化成如何判斷某個區間是否存在:對於任意的 i (L <= i <= R),ai 都能被 ak 整除 了。
顯然,搜索是最普遍的選擇,那這裏我就講講搜索的優化方案: 或許你們會問:不僅要詢問每個區間(O(n)),又要找出其中有沒有 ak 存在(O( n2)),時間復雜度不是 n3 嗎,怎麽過???
優化方案: 優化一: 對於任意的 i (L <= i <= R),ai 都能被 ak 整除 中的 ak,是很好求出來的,就是區間幾個數的最大公約數(gcd),在詢問前預處理一下,線上調用即可。
但是,對於一般的預處理(除前綴和和其他玄學預處理外),復雜度都是O(n2)的,一樣過不了(哭笑不得~)。。。
但是,在序列的維護中,自然想到 線段樹、樹狀數組、rmq 啦~(對於前兩項此題應該也能維護 gcd 吧?!~) 我們了解過的 rmq 都是維護最大最小值的,以其速度快,範圍廣而出名。此題,一樣可以維護區間的 gcd 。 舉個栗子: 4 6 9 若已求出前兩個數的 gcd 為2(rmq[1,1]=2),後一個數的 gcd 為 9(rmq[3,0]=a[3]=9),那區間的 gcd 就是 gcd(2,9)=1 啦~ 預處理時間復雜度降到 O(n log2 n)啦,在搜索時直接 O(1)的復雜度就能算出區間最小公約數(和 rmq 求最大最小值一樣的,換成 gcd 了而已)。 優化二:
通過優化一算出區間的 ak 後,我們就要開始找區間內有沒有這個 ak 了~ 然而我們發現,每個區間的搜索 O(n)不可省,而找 ak 又要 O(n),那豈不是 O(n2)嗎(雖然達不到,但也接近了,加上預處理和二分答案等,絕對過不了) 於是進一步優化: 畢竟我們只找一個數 ak,很容易想到出現一個數就在 flag 數組打個標記,找有沒有 ak 時直接判斷 flag[ak] 是否打過標記就行了。 而區間的移動一次的話,就 O(1)把新的那個數打一個標記,把出區間的那個刪掉標記即可~ 此過程時間復雜度將至 O(1),效率極高,是在某個區間搜某些數時的常用方法,可以經常拿來用。 優化三: 光有優化二是不行的,因為它用空間換時間,會 MLE 的。(畢竟 ak 可達 20 多億,數組開得了那麽多嗎???) 所以,在用了優化二後,常常用這種方法解決空間問題:哈希表~(它倆基本綁定了,哈希是利用無用的空間存有用的數。不會的自己學,不必多說) 但是,這裏的哈希有三種模式: 1. 查詢某數在哈希表中的位置(用於刪標記); 2. 把某數存進哈希表(用於打標記); 3. 查詢某數是否存在(用於查詢區間內是否有 ak)。 於是,我們用哈希表的稀少時間換了大量的空間~~~
所以總體說,預處理 O(n log2 n),二分 O(log2 n*check),在 check 中,區間詢問 O(n),找 ak O(hash)(hash 是哈希的時間復雜度,常數級別) 這樣,我們就把 O(n3)的時間復雜度將至 O(n log2 n+n log2 n*hash),粗略說就是 O(n log2 n),只是有點常數而已,不用卡常都能過(畢竟 2 秒時限)~~~ 下附標程,我就不寫註釋了~
1 uses math; 2 const 3 mo=1000001; 4 var 5 l,r,mid,i,n,j,t,cnt:longint; 6 a,b,h,ans:array[0..1000001] of longint; 7 rmq:array[0..500001,0..21] of longint; 8 function gcd(x,y:longint):longint; 9 begin 10 if y=0 then exit(x) else exit(gcd(y,x mod y)); 11 end; 12 function hash(x,mode:longint):boolean; 13 var 14 k:longint; 15 begin 16 k:=x mod mo; 17 while (h[k]<>0) and (h[k]<>x) do 18 begin 19 inc(k); 20 if k>mo then k:=1; 21 end; 22 if mode=2 then cnt:=k else 23 if mode=1 then h[k]:=x else 24 if h[k]=x then exit(true) else exit(false); 25 end; 26 function check(x:longint):boolean; 27 var 28 l,t,j,flag,r:longint; 29 begin 30 fillchar(h,sizeof(h),0); 31 for i:=1 to x do 32 hash(a[i],1); 33 flag:=0; 34 for l:=1 to n-x+1 do 35 begin 36 hash(a[l-1],2); 37 h[cnt]:=0; 38 r:=l+x-1; 39 hash(a[r],1); 40 t:=trunc(ln(r-l+1)/ln(2)); 41 j:=gcd(rmq[l,t],rmq[r-(1<<t)+1,t]); 42 if hash(j,0) then 43 begin 44 flag:=1; 45 inc(ans[x]); 46 b[ans[x]]:=l; 47 end; 48 end; 49 if flag=0 then exit(false) else exit(true); 50 end; 51 begin 52 readln(n); 53 for i:=1 to n do 54 begin 55 read(a[i]); 56 rmq[i,0]:=a[i]; 57 end; 58 readln; 59 t:=trunc(ln(n)/ln(2))+1; 60 for j:=1 to t do 61 for i:=1 to n do 62 if i+(1<<j)-1<=n then 63 rmq[i,j]:=gcd(rmq[i,j-1],rmq[i+(1<<(j-1)),j-1]); 64 l:=1; // l,r,mid 存的是區間長度 65 r:=n; 66 while l<r do 67 begin 68 mid:=(l+r) div 2+1; 69 if check(mid) then l:=mid else r:=mid-1; 70 end; 71 if ans[l]=0 then 72 begin 73 writeln(n,‘ 0‘); 74 for i:=1 to n-1 do 75 write(i,‘ ‘); 76 writeln(n); 77 halt; 78 end; 79 writeln(ans[l],‘ ‘,l-1); //要輸出價值,就是長度-1 80 for i:=1 to ans[l]-1 do 81 write(b[i],‘ ‘); 82 writeln(b[ans[l]]); 83 end.標程
紀中OJ 01.25【NOIP提高組】模擬 B 組 T2 數字對