标签:实现 its span 分治 code ext register ret size
https://www.luogu.org/problem/CF359D
https://codeforces.com/problemset/problem/359/D
给出一个长度为\(n\)的数列,求最长的区间使区间中存在一个数\(x\)整除区间所有数。
略
第一行输出两个正整数\(cnt\),\(len\)表示满足要求的最长区间的个数与长度。
第二行输出\(cnt\)个升序排列的正整数,表示所有满足要求的最长区间的左端点。
\(n\le3\times10^{5};\)
\(1\le a_{i}\le 10^{6};\)
\(a_{i}\)不重要,由\(n\)的范围可看出\(n^2\)是不行的,可能是\(n\)或\(nlogn\)。
当然在此基础上可以再加个\(log\),事实上正解就是\(nlog^2n\)的。
#include<bits/stdc++.h>
int n,m,ans,a[300001],g[1200001],min[1200001],last[1200001],book[1200001];
inline int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
inline void build_gcd(int k,int l,int r)
{
if(l==r)
{
g[k]=a[l];
return;
}
build_gcd(k<<1,l,l+r>>1);
build_gcd(k<<1|1,(l+r>>1)+1,r);
g[k]=gcd(g[k<<1],g[k<<1|1]);
return;
}
inline int query_gcd(int k,int l,int r,int x,int y)
{
if(r<x||l>y)
return 0;
if(l>=x&&r<=y)
return g[k];
return gcd(query_gcd(k<<1,l,l+r>>1,x,y),query_gcd(k<<1|1,(l+r>>1)+1,r,x,y));
}
inline void build_min(int k,int l,int r)
{
if(l==r)
{
min[k]=a[l];
return;
}
build_min(k<<1,l,l+r>>1);
build_min(k<<1|1,(l+r>>1)+1,r);
min[k]=std::min(min[k<<1],min[k<<1|1]);
return;
}
inline int query_min(int k,int l,int r,int x,int y)
{
if(r<x||l>y)
return 0x7fffffff;
if(l>=x&&r<=y)
return min[k];
return std::min(query_min(k<<1,l,l+r>>1,x,y),query_min(k<<1|1,(l+r>>1)+1,r,x,y));
}
inline int check(int len)
{
int res=0;
memset(book,0,sizeof(book));
for(register int i=1;i<=n-len;i=-~i)
if(!(query_min(1,1,n,i,i+len)^query_gcd(1,1,n,i,i+len)))
book[++res]=i;
return res;
}
int main()
{
scanf("%d",&n);
for(register int i=1;i<=n;i=-~i)
scanf("%d",&a[i]);
build_gcd(1,1,n);
build_min(1,1,n);
int l=0,r=n-1;
while(l<=r)
{
int mid=l+r>>1;
int c=check(mid);
if(c)
{
ans=c;
l=-~mid;
for(register int i=1;i<=ans;i=-~i)
last[i]=book[i];
}
else
r=mid-1;
}
printf("%d %d\n",ans,l-1);
for(register int i=1;i<=ans;i=-~i)
printf("%d ",last[i]);
return 0;
}
区间问题,可以想到用线段树。
如何判断整除?既然\(x\)可以整除区间中所有数,则\(x\)就是本区间的公约数。又因为\(x\)本身也属于这个区间,所以\(x\)就是区间\(gcd\)。显然这个东西可以通过分治到子区间解决。
因为\(x\)整除区间所有数,又可以发现它就是区间最小值。于是建两棵树。
枚举所有区间,找到\(x\)和区间\(gcd\),判断是否相等,计算最大答案。复杂度\(O(n^2logn)\Longleftrightarrow O(跑不过)\)。
线段树这边很难优化了。只能优化查找区间操作。
发现显然答案具有单调性:若某个长区间满足,则它包含的短区间必然满足。故使用二分答案。
建树后二分区间长度,枚举区间左端点,对每个区间计算\(x\)和\(gcd\),判断是否满足。\(O(nlog^2n)\),跑得过。
没有修改操作,实现不难。
标签:实现 its span 分治 code ext register ret size
原文地址:https://www.cnblogs.com/s-t-a-r-d-u-s-t/p/11768207.html