solution-cf359d
较为简单的一道数据结构题。
思路
首先考虑暴力。对于每一个序列中的数 $a_i$ 暴力向后和向前拓展,虽然在随机数据跑得飞快,但是最坏可以被卡到 $O(n^2)$。
稍微观察一下题面,发现是求区间 $\operatorname{gcd}$ 。回想一下 $\operatorname{gcd}$ 的性质,我们发现 $\operatorname{gcd}$ 满足结合律。满足结合律就可以考虑使用一些数据结构进行优化。这里我们可以使用线段树之类的数据结构。考虑对于每个可能长度都进行一次枚举,再 $\log(n)$ 求出区间 $\operatorname{gcd}$ ,区间最小值,如果区间 $\operatorname{gcd}$ 等于区间最小值,那这个区间就是可行的。复杂度 $O(n \log n)$。
可惜以上复杂度并不能够顺利通过此题,但是我们稍加思考,即可发现可行的区间长度满足单调性,考虑使用二分答案进行优化,即可将复杂度降至 $O(n \log^2 n)$。
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int maxn = 500000+5;
int gcd(int a, int b){
if(b == 0){
return a;
}
return gcd(b,a%b);
}
int n;
int a[maxn], f[maxn][25], f2[maxn][25];
void init(){
for(int i = 1; i <= n; i++) f[i][0] = a[i];
for(int j = 1; j <= 25; j++)
for(int i = 1; i +(1 <<j) -1 <= n; ++i){
f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
}
void init2(){
for(int i = 1; i <= n; i++) f2[i][0] = a[i];
for(int j = 1; j <= 25; j++)
for(int i = 1; i +(1 <<j) -1 <= n; ++i){
f2[i][j] = gcd(f2[i][j-1], f2[i+(1<<(j-1))][j-1]);
}
}
int cnt;
int ans[500005];
int q(int l, int r){
int k = log2(r-l+1);
return min(f[l][k], f[r-(1<<k)+1][k]);
}
int q2(int l, int r){
int k = log2(r-l+1);
return gcd(f2[l][k], f2[r-(1<<k)+1][k]);
}
bool check(int x){
for(int i = 1; i <= n-x+1; i++){
if(q2(i, i+x-1) == q(i, i+x-1)){
ans[++cnt] = i;
}
}
if(cnt) return 1;
return 0;
}
signed main()
{ ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i = 1; i <= n; i++) cin>>a[i];
memset(f, 0x3f, sizeof(f));
init();
init2();
int L = 1, R = n;
while(L <= R){
int mid = (L+R)/2;
if(check(mid)){
L = mid + 1;
cnt = 0;
}else{
R = mid-1;
cnt = 0;
}
}
check(R);
cout<<cnt<<' '<<R-1<<endl;
for(int i = 1; i <= cnt; i++){
cout<<ans[i]<<' ';
}
return 0;
}
相关练习
CF475D