倍数区间
倍数区间
先定义 \(i\) 的区间表示最长的区间 \([l,r]\) 满足 \(\forall k\in[l,r]\;a_i|a_k\) 且 \(l\le i\le r\)。
首先需要知道几个性质
- 如果 \(a_j\) 是 \(a_i\) 的倍数那么除非 \(a_j=a_i\),否则 \(a_i\) 不会是 \(a_j\) 的倍数
- 对于 \(a_i,a_j(i<j)\),如果 \(\forall k\in[i+1,j]\;a_i|a_k\) 且 \(a_i\not =a_j\) 那么 \(i\) 的区间一定比 \(j\) 的大。
- 对于 \(a_i,a_j(j<i)\) 如果 \(\forall k\in[j,i-1]\; a_i|a_k\) 且 \(a_i\not = a_j\) 那么 \(i\) 的区间一定比 \(j\) 的大。
显然第一条成立。下面证明二三条。
先来看第二条,由第一条可知 \(a_j\) 不是 \(a_i\) 倍数,所以 \(j\) 的区间的左端点最优只能到 \(i+1\)。又因为 \(a_j\) 是 \(a_i\) 的倍数,所以对于 \(\forall x\in R\) 如果 \(a_j|x\) 那么 \(a_i|x\) 但是若 \(a_i|x\) 并不一定又 \(a_j|x\) 故 \(i\) 的右端点一定大于等于 \(j\) 的右端点。由此可得 \(i\) 的区间一定大于 \(j\) 的区间。
同理可得第三条。
有了这几条性质我们就可以快速处理出可能是最佳答案的点的区间。设立一个指针可以在 \(O(n)\) 的时间复杂度内求出区间。
最后记得去重,总时间复杂度是 \(O(n)\)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5+5;
int n,a[MAXN],L[MAXN],R[MAXN],ans,cnt,fin[MAXN];
int b[MAXN];
vector <int> vec;
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
memset(L,0x3f,sizeof L);
memset(R,0,sizeof R);
int r=1;
for(int i=1;i<=n;++i)
{
while(r+1<=n&&a[r+1]%a[i]==0)
{
++r;
if(a[r]==a[i]) vec.push_back(r);
}
for(int j=0;j<vec.size();++j)
R[vec[j]]=r;
vec.clear();
R[i]=r;
i=r;
}
int l=n;
for(int i=n;i>=1;--i)
{
while(l-1>=1&&a[l-1]%a[i]==0)
{
--l;
if(a[l]==a[i]) vec.push_back(l);
}
for(int j=0;j<vec.size();++j)
L[vec[j]]=l;
vec.clear();
L[i]=l;
i=l;
}
for(int i=1;i<=n;++i)
{
ans=max(ans,R[i]-L[i]);
if(L[i]<=n)fin[L[i]]=max(fin[L[i]],R[i]);
}
for(int i=1;i<=n;++i)
if(fin[i]-i==ans) b[++b[0]]=i;
printf("%d %d\n",b[0],ans);
for(int i=1;i<=b[0];++i)
printf("%d ",b[i]);
return 0;
}
路漫漫其修远兮,吾将上下而求索。