P7244 章节划分
这一整场比赛质量都很高,可以去看看,link
比赛的时候被这题杀了/ll。赛后看完题解发现我是个傻逼,补完题回头一看提交数发现我是傻逼石锤了。
首先应该想到答案一定是最大数的约数。暴力枚举只有 \(240\) 种情况,所以对每种情况以接近线性的复杂度判断是否合法即可。
这个恰好 \(k\) 比较鬼畜。\(k\) 这一维显然不改开进状态。显然没有凸性所以不要想wqs二分。
但是没想到就是个pj的贪心啊
注意到相邻两个合法的段必然可以合并。所以只需要判断最多能分几段就好了!!!
假设我们要判断 \(\gcd=x\) 是否可行,令 \(dp_i\) 表示到 \(i\) 为之最多分几段。
\(dp_i=\max\limits_{0\le j<i}(dp_j+[\max\limits_{j<k\le i}\{a_k\}\%x=0])\)
非常显然有很多转移是白费的,具体来说,如果前面第一个大于 \(a_i\) 的数为 \(a_{pre}\) ,那么 \([0,pre]\) 之内的转移都不优于 \([pre,i)\) 之间的转移,因为 \(pre\) 这地方分一段显然更优。
而 \([pre,i)\) 之内的 \(\max\) 必然是 \(a_i\) ,所以直接单调栈预处理 \(pre\) ,搞颗线段树维护一下区间最值,就没了woc这么智障的题考场上没切,我就是逊啊
不过提一句,边界很烦,要小心。
感谢 \(\color{black}{\texttt{G}}\color{red}{\texttt{eorge1123}}\) 的 hack数据
3 2
8 6 9
答案是 \(1\) 。
const int N=100005;
const int T=N<<2;
int n,k,a[N],MAX_A,dp[N],d[N],stk[N],top,pre[N],mxv[T];
#define lc (p<<1)
#define rc (p<<1|1)
void change(int pos,int k,int l=1,int r=n,int p=1){
ckmax(mxv[p],k);
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)change(pos,k,l,mid,lc);
else change(pos,k,mid+1,r,rc);
}
int query_max(int ql,int qr,int l=1,int r=n,int p=1){
if(ql<=l&&r<=qr)return mxv[p];
int mid=(l+r)>>1;
if(qr<=mid)return query_max(ql,qr,l,mid,lc);
if(mid<ql)return query_max(ql,qr,mid+1,r,rc);
return max(query_max(ql,qr,l,mid,lc),query_max(ql,qr,mid+1,r,rc));
}
bool check(int x){
memset(dp,-0x3f,(n+1)<<2),dp[0]=0,memset(mxv,-0x3f,sizeof(mxv));
for(int i=1;i<=n;++i){
if(pre[i]||a[i]%x==0)dp[i]=dp[pre[i]];
if(a[i]%x==0){
if(i==1)dp[i]=1;
else {
ckmax(dp[i],query_max(max(1,pre[i]),i-1)+1);
if(!pre[i])ckmax(dp[i],1);
}
}
change(i,dp[i]);
}
return dp[n]>=k;
}
signed main(){
n=read(),k=read();
rep(i,1,n)ckmax(MAX_A,a[i]=read());
for(int i=1;i<=n;++i){
while(top&&a[stk[top]]<=a[i])--top;
pre[i]=stk[top],stk[++top]=i;
}
for(int i=1;i*i<=MAX_A;++i){
if(MAX_A%i)continue;
d[++d[0]]=i;
if(i*i!=MAX_A)d[++d[0]]=MAX_A/i;
}
sort(d+1,d+d[0]+1,greater<int>());
for(int i=1;i<=d[0];++i)if(check(d[i]))return printf("%d\n",d[i]),0;
cout<<-1;
return 0;
}
路漫漫其修远兮,吾将上下而求索