題目大意:
給出長度爲的區間,
要求找一段最長的區間滿足
問最長的區間長度和區間個數,並依次輸出區間左端點。
分析:
二分區間長度,
當前二分到,
因爲當長度爲時不存在解時,那麼中顯然必定無解
所以x有解時向右二分否則向左
對於一個枚舉的長度而言,我們都去枚舉左端點,
看是否存在滿足條件,找到就退
然後判斷是否滿足可以通過表預處理區間最小值跟區間快速查詢
代碼:
#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;
}