声明


数字对

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),完全过不了。

  如:

  500000

  后面 500000 个 1

  此时时间复杂度为O(5000002),不知道运行到猴年马月,直接卡掉水法。

 

  接下来看正解(可能有其他更简单的方法,但这种方法绝对不会被卡掉!): 二分+rmq+hash

 

 
  分析:
    
    看到了最大价值,即求最优解,脑海中立马想到了:贪心、dp、优化的搜索(或暴力)和 二分答案 。显然,此题是在序列中操作,数据范围大,贪心条件不满足,明显的二分答案!!!
    于是用二分答案求出序列的最大长度,但如何判断答案 mid 的可行性和计算序列个数与开头位置呢?
 
    这样,问题就转化成如何判断某个区间是否存在:对于任意的 i (L <= i <= R),ai 都能被 ak 整除 了。
    显然,搜索是最普遍的选择,那这里我就讲讲搜索的优化方案:
 
    或许你们会问:不仅要询问每个区间(O(n)),又要找出其中有没有 ak 存在(O( n2)),时间复杂度不是 n吗,怎么过???
 

 
  优化方案:
  
    优化一:
 
      对于任意的 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 秒时限)~~~
  
  下附标程,我就不写注释了~
    

 uses math;
const
mo=;
var
l,r,mid,i,n,j,t,cnt:longint;
a,b,h,ans:array[..] of longint;
rmq:array[..,..] of longint;
function gcd(x,y:longint):longint;
begin
if y= then exit(x) else exit(gcd(y,x mod y));
end;
function hash(x,mode:longint):boolean;
var
k:longint;
begin
k:=x mod mo;
while (h[k]<>) and (h[k]<>x) do
begin
inc(k);
if k>mo then k:=;
end;
if mode= then cnt:=k else
if mode= then h[k]:=x else
if h[k]=x then exit(true) else exit(false);
end;
function check(x:longint):boolean;
var
l,t,j,flag,r:longint;
begin
fillchar(h,sizeof(h),);
for i:= to x do
hash(a[i],);
flag:=;
for l:= to n-x+ do
begin
hash(a[l-],);
h[cnt]:=;
r:=l+x-;
hash(a[r],);
t:=trunc(ln(r-l+)/ln());
j:=gcd(rmq[l,t],rmq[r-(<<t)+,t]);
if hash(j,) then
begin
flag:=;
inc(ans[x]);
b[ans[x]]:=l;
end;
end;
if flag= then exit(false) else exit(true);
end;
begin
readln(n);
for i:= to n do
begin
read(a[i]);
rmq[i,]:=a[i];
end;
readln;
t:=trunc(ln(n)/ln())+;
for j:= to t do
for i:= to n do
if i+(<<j)-<=n then
rmq[i,j]:=gcd(rmq[i,j-],rmq[i+(<<(j-)),j-]);
l:=; // l,r,mid 存的是区间长度
r:=n;
while l<r do
begin
mid:=(l+r) div +;
if check(mid) then l:=mid else r:=mid-;
end;
if ans[l]= then
begin
writeln(n,'');
for i:= to n- do
write(i,' ');
writeln(n);
halt;
end;
writeln(ans[l],' ',l-); //要输出价值,就是长度-
for i:= to ans[l]- do
write(b[i],' ');
writeln(b[ans[l]]);
end.

标程

 

纪中OJ 2019.01.25【NOIP提高组】模拟 B 组 T2 数字对的更多相关文章

  1. 纪中OJ 2019.02.15【NOIP提高组】模拟 B 组 梦回三国 比赛题解(第一个)

    声明 旁边的同学小 H(胡)对我说: “哟,比赛拿了 140,强!要知道,如果哥第三题 AC 了,哥就 230 了,你个废柴!!!(比赛实际分数 130 额呵)” 顿时,千万草泥马从我心中奔腾而过:你 ...

  2. 【纪中集训】2019.08.01【NOIP提高组】模拟 A 组TJ

    T1 Description 给定一个\(N*N(N≤8)\)的矩阵,每一格有一个0~5的颜色.每次可将左上角的格子所在连通块变为一种颜色,求最少操作数. Solution IDA*=启发式迭代加深 ...

  3. IntelliJ IDEA 2018.3.3配置 Tomcat 9,控制台出现中文乱码 “淇℃伅”(2019/01/25)

    (win10系统) 全新idea配置全新版本Tomcat突遇 “淇℃伅”,网上大部分解决方案均已失效 似乎是idea与Tomcat命令行输出格式不一致所致,千辛万苦在某一小角落发现这个方法,一针见血, ...

  4. 【纪中集训2019.3.27】【集训队互测2018】小A的旅行(白)

    题目 描述 ​ \(0-n-1\)的图,满足\(n\)是\(2\)的整数次幂, $ i \to j $ 有 $ A_{i,j} $ 条路径: ​ 一条路径的愉悦值定义为起点和终点编号的\(and\)值 ...

  5. 【纪中集训2019.3.23】Deadline

    题意 描述 一个二分图\((A,B)\),每个点额外有一个颜色0或者1: 匹配时,只能相同颜色的点匹配: 给出\(A\)中的颜色,问如何分配\(B\)种的颜色使得\((A,B)\)的最大匹配最小: 范 ...

  6. 【纪中集训2019.3.12】Z的礼物

    题意 已知\(a_{i} = \sum_{j=1}^{i} \{^{i} _{j} \}b_{j}\), 给出\(a_{1} 到 a_{n}\) : 求\(b_{l} 到 b_{r}\)在\(1e9+ ...

  7. 【纪中集训2019.3.12】Mas的仙人掌

    题意: ​ 给出一棵\(n\)个点的树,需要加\(m\)条边,每条边脱落的概率为\(p_{i}\) ,求加入的边在最后形成图中仅在一个简单环上的边数的期望: \(1 \le n \ , m \le 1 ...

  8. 【2019.7.25 NOIP模拟赛 T3】树(tree)(dfs序列上开线段树)

    没有换根操作 考虑如果没有换根操作,我们该怎么做. 我们可以求出原树的\(dfs\)序列,然后开线段树维护. 对于修改操作,我们可以倍增求\(LCA\),然后在线段树上修改子树内的值. 对于询问操作, ...

  9. 【2019.7.25 NOIP模拟赛 T1】变换(change)(思维+大分类讨论)

    几个性质 我们通过推式子可以发现: \[B⇒AC⇒AAB⇒AAAC⇒C\] \[C⇒AB⇒AAC⇒AAAB⇒B\] 也就是说: 性质一: \(B,C\)可以相互转换. 则我们再次推式子可以发现: \[ ...

随机推荐

  1. ajax post data 获取不到数据

    ajax post  data  获取不到数据,注意 content-type的设置 .post/get关于 jQuery data 传递数据.网上各种获取不到数据,乱码之类的.好吧今天我也遇到了,网 ...

  2. August 10th 2017 Week 32nd Thursday

    Break through the psychological barrier to surpass themselves. 突破心理障碍,才能超越自己. To break through those ...

  3. Python语言特性

    1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun ...

  4. Linux命令--文件处理

    touch命令 Linux touch命令用于修改文件或者目录的时间属性,包括存取时间和更改时间.若文件不存在,系统会建立一个新的文件. ls -l 可以显示档案的时间记录. 语法 touch [-a ...

  5. PhoneGap获取设备信息

    一. 获取设备信息的方法列表(如果没有或者检测不出来就显示undefined) 1.device.name              设备名称(一些国产机检测不出来) 2.device.model   ...

  6. POJ-2452 Sticks Problem 二分+RMQ

    题目链接: https://cn.vjudge.net/problem/POJ-2452 题目大意: 给出一个数组a,求最大的j-i满足 i<j && a[i] ... a[j] ...

  7. PHP imagechar() 图形验证码 字体太小问题

    bool imagechar ( resource $image , int $font , int $x , int $y , string $c , int$color ) imagechar() ...

  8. 解决 php7下 igbinary_unserialize_ref: invalid reference 的bug

    最近组内升级了PHP7,某个接口偶发502,看了下php的错误日志如下: igbinary_unserialize_ref: invalid reference >= Memcached::ge ...

  9. ZooKeeper学习之路 (六)ZooKeeper API的简单使用(二)级联删除与创建

    编程思维训练 1.级联查看某节点下所有节点及节点值 2.删除一个节点,不管有有没有任何子节点 3.级联创建任意节点 4.清空子节点 ZKTest.java public class ZKTest { ...

  10. Redis入门(一)---安装

    一.Ubuntu安装 1.命令行安装 sudo apt-get install redis-server 2.启动redis服务(安装完成后自动启动) sudo /etc/init.d/redis-s ...