正题
题目链接:https://www.luogu.com.cn/problem/P7244?contestId=38911
题目大意
nnn个数字,分成连续非空的kkk段要求每一段的最大值的gcdgcdgcd最大。
解题思路
首先答案一定是最大值的约数,这些数不多我们可以枚举这些数xxx。然后我们称xxx的倍数的位置为关键点。
现在我们要求在选取的全都是关键点的情况下最多能够选取的关键点数量
我们先找出最大的数(第一次肯定是最大的那个数)然后如果这个数是xxx的倍数就递归左右两边把数量加起来。如果不是我们就比较这个点和区间的左右两边(一定是空的或者是关键点),如果一边比这个数要大就可以递归另一边(那一边的那个数区间延伸过来),如果两边都比这个数要大就取两边的最大值即可。
RMQRMQRMQ维护区间最大的位置即可
时间复杂度O(nlogn+namax)O(n\log n+n\sqrt {a_{max}})O(nlogn+namax)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,T=20;
int n,m,a[N],f[N][T],lg[N],ans;
int rmq(int l,int r){int z=lg[r-l+1];return (a[f[l][z]]>a[f[r+1-(1<<z)][z]])?f[l][z]:f[r+1-(1<<z)][z];
}
int calc(int l,int r,int pl,int pr,int x){if(l>r)return 0;int k=rmq(l,r),tmp=0;if(a[k]%x==0)return calc(l,k-1,pl,a[k],x)+calc(k+1,r,a[k],pr,x)+1;if(a[k]<pr)tmp=max(tmp,calc(l,k-1,pl,pr,x));if(a[k]<pl)tmp=max(tmp,calc(k+1,r,pl,pr,x));if(a[k]>pl&&a[k]>pr)return -1e9;return tmp;
}
void solve(int x){int k=rmq(1,n);if(calc(1,k-1,0,a[k],x)+calc(k+1,n,a[k],0,x)+1>=m)ans=max(ans,x);return;
}
int main()
{scanf("%d%d",&n,&m);int w=0;lg[0]=-1;for(int i=1;i<=n;i++){scanf("%d",&a[i]);w=max(w,a[i]);f[i][0]=i;lg[i]=lg[i/2]+1;}for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++){if(a[f[i][j-1]]>a[f[i+(1<<j-1)][j-1]])f[i][j]=f[i][j-1];else f[i][j]=f[i+(1<<j-1)][j-1];}for(int i=1;i*i<=w;i++){if(w%i==0){solve(i);if(i*i!=w)solve(w/i);}}printf("%d\n",ans);return 0;
}