数字对

2 篇文章 0 订阅

题目描述
小H是个善于思考的学生,现在她又在思考一个有关序列的问题。
她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。
这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R),ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R - L。
小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。
输入
第一行,一个整数n.
第二行,n个整数,代表ai.
输出
第一行两个整数,num和val,表示价值最大的特殊区间的个数以及最大价值。
第二行num个整数,按升序输出每个价值最大的特殊区间的L.
样例输入
5
4 6 9 3 6
样例输出
1 3
2
提示
【样例输入2】
5
2 3 5 7 11
【样例输出2】
5 0
1 2 3 4 5
【数据范围】
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.

“ai都能被ak整除”,换句话说就是这段区间的最小值为这段区间的gcd。于是我们二分答案,用RMQ来寻找区间最小值和区间gcd,判断是否合法。详见代码。

var
n,i,l,r,mid,sum,k:longint;
fa,mini,g:array[0..500005,0..18] of longint;
a,ans:array[0..500005] of longint;
function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;

function gcd(a,b:longint):longint;
begin
  if b=0 then exit(a);
  if a mod b=0 then exit(b) else exit(gcd(b,a mod b));
end;

function check(len:longint):longint;
var
i,l,r,k,num,gc,mimi:longint;
begin
  sum:=0;
  for i:=1 to n-len do
  begin
    l:=i;
    r:=i+len;
    k:=len+1;
    for num:=18 downto 0 do
      if k >> num and 1=1 then  break;
    k:=1<<num;
    mimi:=min(mini[l,num],mini[r-k+1,num]);
    gc:=gcd(g[l,num],g[r-k+1,num]);
    if mimi=gc then
    begin
      sum:=sum+1;
      ans[sum]:=l;
    end;
  end;
  exit(sum);
end;


begin
  readln(n);
  for i:=1 to n do
  begin
    read(a[i]);
    fa[i,0]:=i+1;
    mini[i,0]:=a[i];
    g[i,0]:=a[i];
  end;
  for k:=1 to 18 do
    for i:=1 to n do
    begin
      fa[i,k]:=fa[fa[i,k-1],k-1];
      mini[i,k]:=min(mini[i,k-1],mini[fa[i,k-1],k-1]);
      g[i,k]:=gcd(g[i,k-1],g[fa[i,k-1],k-1]);
    end;
  l:=0;
  r:=n-1;
  while l<>r do
  begin
    mid:=(l+r+1) div 2;
    if check(mid)<>0 then l:=mid
                     else r:=mid-1;
  end;
  check(l);
  writeln(sum,' ',l);
  for i:=1 to sum-1 do
    write(ans[i],' ');
  write(ans[sum]);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值