倍数区间

倍数区间


​ 先定义 \(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;
}

posted @ 2021-11-05 09:16  夜空之星  阅读(147)  评论(0编辑  收藏  举报