6872. 【2020.11.19提高组模拟】倍数区间

给这道垃圾水题写写题解
毕竟自己一开始没有想到栈
设 l e f t [ i ] − r i g h t [ i ] 表 示 对 于 i 的 最 长 区 间 设left[i]-right[i]表示对于i的最长区间 left[i]right[i]i
如何高效的维护 l e f t left left数组?
我们决定不低效的从 i i i一位位往左移,因为 l e f t left left本身就保存了我们所需要的信息!
如果 a [ i ] ∣ a [ i − 1 ] a[i]|a[i-1] a[i]a[i1] i i i完全可以跳到 l e f t [ i − 1 ] left[i-1] left[i1],因为 l e f t [ i − 1 ] − i left[i-1]-i left[i1]i之间的数既然是 a [ i − 1 ] a[i-1] a[i1]的倍数,也势必是 a [ i ] a[i] a[i]的倍数
这样 l e f t left left的维护我们就解决了, r i g h t right right同理
证明时间复杂度(以 l e f t left left为例)
对于 a [ i ] a[i] a[i]来说,它已经处理好了它的消息
这次信息再被调用时,目前的 a [ j ] a[j] a[j]最大是 a [ i ] a[i] a[i] 1 / 2 1/2 1/2
然后 a [ i ] a[i] a[i]的消息没用了, a [ j ] a[j] a[j]的消息等待下一个它的约数
每次信息调用次数是 l o g log log
所以时间复杂度是 O ( 31 n ) O(31n) O31n,实际上远远达不到,实测挺快的

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500005
using namespace std;
int left[N],right[N],a[N],bz[N],pr[N];
int i,j,k,m,n,o,p,l,s,t;
void read(int &x)
{
	char ch=getchar();x=0;
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
}
int main()
{
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	read(n);
	for (i=1;i<=n;i++) read(a[i]);	
	for (i=1;i<=n;i++)
	{
		o=i;left[i]=i;
		while (a[o-1]%a[i]==0&&o>1) 
			left[i]=left[o-1],o=left[i];
	}
	for (i=n;i>=1;i--)
	{
		o=i;right[i]=i;
		while (a[o+1]%a[i]==0&&o<n) right[i]=right[o+1],o=right[i];
	}
	s=0;
	for (i=1;i<=n;i++) s=max(s,right[i]-left[i]);
	for (i=1;i<=n;i++)
		if (right[i]-left[i]==s&&!bz[left[i]]) pr[++pr[0]]=left[i],bz[left[i]]=1,t++,bz[left[i]]=1;
	printf("%d %d\n",t,s);
	for (i=1;i<=pr[0];i++) printf("%d ",pr[i]);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值