9.19
思路:
std的题解:
算法一
直接上暴力模拟,枚举每一个区间,时间复杂度O(n3)。
期望得分30分。
算法二
枚举一个左端点,然后开一个桶,再往右扫过去。
每一次加入一个数更新一下当前的gcd,然后看区间中是否存在这个数。
时间复杂度O(n2 log(n))
期望得分60分。
算法三
开始用ST表预处理一下区间gcd,以及区间最小值。
二分区间长度,然后每一次check扫过去,看是否有区间最小值和gcd相等。
时间复杂度O(n log2(n))
如果你写得丑,或者没有开long long,期望得分80分。
如果你颜值高,然后又聪明机智(就像NYG一样),期望得分100分。
然而std并没有提到我这种歪门邪道。。。
考虑找到任意一个数作为k,然后向两边扩张。如果被包含进区间了,就说明ak|ai,所以由ai作为k的扩展区间一定被ak的区间真包含,(自己YY一下就知道了)。所以ai就不用枚举啦!这样一来每个数最多会被统计1次,所以就是O(n)的啦:-)
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 500010;
int n, idc=0, cnt=0, maxn=0;
int vis[N], ans[N];
struct AA{
int val, pos, flag;
}a[N], b[N];
bool cmp(AA aa, AA bb){
return aa.val < bb.val;
}
bool cmp1(int aa, int bb){
return aa < bb;
}
int main(){
freopen ("point.in", "r", stdin);
freopen ("point.out", "w", stdout);
scanf("%d", &n);
for(register int i=1; i<=n; i++){
scanf("%d", &a[i].val);
a[i].pos = i;
b[i].val = a[i].val;
b[i].pos = a[i].pos;
}
sort(b+1, b+1+n, cmp);
while (true){
while (a[b[++idc].pos].flag == 1);
if(idc == n+1) break;
int p = b[idc].pos;
int cc = p; a[p].flag = 1;
while(a[--cc].val % a[p].val == 0 && cc >= 1) {
a[cc].flag = 1;
}
ans[++cnt] = cc + 1;
cc = p;
while(a[++cc].val % a[p].val == 0 && cc <= n) {
a[cc].flag = 1;
}
int L = cc - 1 - ans[cnt];
if(L > maxn){
maxn = L; ans[1] = ans[cnt]; cnt = 1;
}
if(L < maxn) cnt--;
}
printf("%d %d\n", cnt, maxn);
sort(ans+1, ans+1+cnt, cmp1);
for(register int i=1; i<=cnt; i++){
printf("%d ", ans[i]);
}
return 0;
}