给这道垃圾水题写写题解
毕竟自己一开始没有想到栈
设
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[i−1],
i
i
i完全可以跳到
l
e
f
t
[
i
−
1
]
left[i-1]
left[i−1],因为
l
e
f
t
[
i
−
1
]
−
i
left[i-1]-i
left[i−1]−i之间的数既然是
a
[
i
−
1
]
a[i-1]
a[i−1]的倍数,也势必是
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)
O(31n),实际上远远达不到,实测挺快的
#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;
}