题解 CF359D
Posted s-t-a-r-d-u-s-t
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 CF359D相关的知识,希望对你有一定的参考价值。
(CF359D;Pair;of;Numbers)
https://www.luogu.org/problem/CF359D
https://codeforces.com/problemset/problem/359/D
题目描述
给出一个长度为(n)的数列,求最长的区间使区间中存在一个数(x)整除区间所有数。
输入格式
略
输出格式
第一行输出两个正整数(cnt),(len)表示满足要求的最长区间的个数与长度。
第二行输出(cnt)个升序排列的正整数,表示所有满足要求的最长区间的左端点。
数据范围分析
(nle3 imes10^{5};)
(1le a_{i}le 10^{6};)
(a_{i})不重要,由(n)的范围可看出(n^2)是不行的,可能是(n)或(nlogn)。
当然在此基础上可以再加个(log),事实上正解就是(nlog^2n)的。
(code)
#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
",ans,l-1);
for(register int i=1;i<=ans;i=-~i)
printf("%d ",last[i]);
return 0;
}
(solution)
区间问题,可以想到用线段树。
如何判断整除?既然(x)可以整除区间中所有数,则(x)就是本区间的公约数。又因为(x)本身也属于这个区间,所以(x)就是区间(gcd)。显然这个东西可以通过分治到子区间解决。
因为(x)整除区间所有数,又可以发现它就是区间最小值。于是建两棵树。
枚举所有区间,找到(x)和区间(gcd),判断是否相等,计算最大答案。复杂度(O(n^2logn)Longleftrightarrow O(跑不过))。
线段树这边很难优化了。只能优化查找区间操作。
发现显然答案具有单调性:若某个长区间满足,则它包含的短区间必然满足。故使用二分答案。
建树后二分区间长度,枚举区间左端点,对每个区间计算(x)和(gcd),判断是否满足。(O(nlog^2n)),跑得过。
没有修改操作,实现不难。
以上是关于题解 CF359D的主要内容,如果未能解决你的问题,请参考以下文章