数字对html
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
输出1:
1 3
2
输出2:
5 0
1 2 3 4 5
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.
最近发现有用暴力等水法A掉这题的,如从头至尾枚举 ak ,而后再向两边搜索的水法。数组
然而若是全部的 ai 都相同的话,时间复杂度就为O(n2),彻底过不了。ide
如:优化
500000spa
后面 500000 个 1code
此时时间复杂度为O(5000002),不知道运行到猴年马月,直接卡掉水法。htm
接下来看正解(可能有其余更简单的方法,但这种方法绝对不会被卡掉!): 二分+rmq+hashblog
分析:
看到了最大价值,即求最优解,脑海中立马想到了:贪心、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 log
2 n),二分 O(log
2 n*check),在 check 中,区间询问 O(n),找 ak O(hash)(hash 是哈希的时间复杂度,常数级别)
这样,咱们就把 O(n
3)的时间复杂度将至 O(n log
2 n+n log
2 n*hash),粗略说就是 O(n log
2 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.
标程