题目大意:
给出长度为
n
n
n的区间
a
a
a,
要求找一段最长的区间满足
区
间
g
c
d
=
区
间
m
i
n
区间gcd=区间min
区间gcd=区间min
问最长的区间长度和区间个数,并依次输出区间左端点。
分析:
二分区间长度,
当前二分到
x
x
x,
因为当长度为
x
x
x时不存在解时,那么
[
x
+
1
,
n
]
[x+1,n]
[x+1,n]中显然必定无解
所以x有解时向右二分否则向左
对于一个枚举的长度而言,我们都去枚举左端点
l
l
l,
看是否存在
[
l
,
l
+
x
−
1
]
[l,l+x-1]
[l,l+x−1]满足条件,找到就退
然后判断是否满足可以通过
s
t
st
st表预处理区间最小值跟区间
g
c
d
gcd
gcd快速查询
代码:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define lson(x) x * 2
#define rson(x) x * 2 + 1
#define N 500005
using namespace std;
typedef long long ll;
struct Node {
int cgcd, cmin;
}f[N][20];
int clg[N], ans[N], orz[20], a[N], n, cnt, anslen, anscnt, dmin, dgcd, dlog;
int Gcd(int a, int b) {
return b ? Gcd(b, a % b) : a;
}
int main() {
freopen("point.in", "r", stdin);
freopen("point.out", "w", stdout);
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &a[i]), f[i][0].cgcd = f[i][0].cmin = a[i];
clg[2] = 1; rep(i, 3, n) clg[i] = clg[i / 2] + 1;
rep(i, 0, 20) orz[i] = 1<<i;
rep(i, 1, 20)
rep(j, 1, n)
if (j + orz[i] - 1 <= n)
f[j][i].cgcd = Gcd(f[j][i - 1].cgcd, f[j + orz[i - 1]][i - 1].cgcd),
f[j][i].cmin = min(f[j][i - 1].cmin, f[j + orz[i - 1]][i - 1].cmin);
int L = 1, R = n;
while (L <= R) {
int len = (L + R) >> 1;
cnt = 0;
rep(i, 1, n - len + 1) { //[i,i+len-1]
dlog = clg[len];
dgcd = Gcd(f[i][dlog].cgcd, f[i + len - orz[dlog]][dlog].cgcd);
dmin = min(f[i][dlog].cmin, f[i + len - orz[dlog]][dlog].cmin);
if (dgcd == dmin) { ++cnt; break; }
}
if (cnt) anslen = len, L = len + 1; else R = len - 1;
}
anscnt = 0;
rep(i, 1, n - anslen + 1) { //[i,i+len-1]
dlog = clg[anslen];
dgcd = Gcd(f[i][dlog].cgcd, f[i + anslen - orz[dlog]][dlog].cgcd);
dmin = min(f[i][dlog].cmin, f[i + anslen - orz[dlog]][dlog].cmin);
if (dgcd == dmin) ans[++anscnt] = i;
}
printf("%d %d\n", anscnt, anslen - 1);
rep(i, 1, anscnt) printf("%d ", ans[i]); printf("\n");
return 0;
}