#笛卡尔树,dp#洛谷 7244 章节划分

题目


分析

考虑段数受到答案限制,而答案为最大值的约数,那么枚举答案,
\(dp[i]\)表示前\(i\)个位置分完最多可以分多少段只要\(dp[n]\geq k\)即合法。
那么\(dp[i]=\max\{dp[j]\}+1,ans|\max\{a[j\sim i]\}\),这可以用数据结构实现,
当然可以用笛卡尔树来做,维护一个大根堆,如果最大值为答案的约数那就将两边分开,
否则将一边合并到左右两边,然后判断能否分出\(k\)


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#define rr register
using namespace std;
const int N=100011; int a[N],n,m,x,bl;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed max(int a,int b){return a>b?a:b;}
struct Descartes{
	int st[N],ls[N],rs[N],top,last,root;
	inline void build(){
		top=0,last=0;
		for (rr int i=1;i<=n;++i){
			while (a[st[top]]<a[i]) --top;
			if (top<last) ls[i]=st[top+1];
			if (top) rs[st[top]]=i;
			st[last=++top]=i;
		}
		root=st[1];	
	}
	inline signed query(int now,int l,int r){
		if (!now) return 0; 
		if (a[now]%x==0) return query(ls[now],l,now-1)+query(rs[now],now+1,r)+1;
		rr int ans=0;
		if (l>1) ans=max(ans,query(rs[now],now+1,r));
		if (r<n) ans=max(ans,query(ls[now],l,now-1));
		return ans;
	}
}Tre;
signed main(){
	n=iut(),m=iut();
	for (rr int i=1;i<=n;++i) a[0]=max(a[0],a[i]=iut());
	Tre.build(),bl=sqrt(a[0]);
	for (rr int i=1;i<=bl;++i)
	if (a[0]%i==0){
		x=a[0]/i;
		if (Tre.query(Tre.root,1,n)>=m)
		    return !printf("%d",x);
	}
	for (rr int i=bl;i;--i)
	if (a[0]%i==0){
		x=i;
		if (Tre.query(Tre.root,1,n)>=m)
		    return !printf("%d",x);
	}
	return 0;
}
posted @ 2021-05-07 10:58  lemondinosaur  阅读(90)  评论(0编辑  收藏  举报