CF1175G

CF1175G - Yet Another Partiton Problem

题目大意

给定序列(a_i),现在将其分成(k)段,每段([l,r])的权值定义为((r-l+1)max{a_{l..r}})

求最小化权值总和


分析

显然有(mathbb{Naive})(dp)

(dp_{i,j})表示前(i)个分了(j)段的答案,直接做复杂度为(O(n^2k))

优化它有几种常见思路:

1.贪心简化决策

设每个段最大值(b_i),则一个段不能向左边扩展的条件是

两端的(b_{i-1}<b_i)(扩展会亏),或者(a_{l_i-1}>b_i)(扩展会改变(b_i)

这样能稍微限制一下转移,然而并不好优化

2.决策单调性

枚举区间进行决策的问题通常具有单调性

于是尝试通过分治决策单调性优化到(O(nklog n))

然而已经被垃圾数据击毙 尝试证明这是假的

3.斜率优化

考虑确定(max a_i)之后,两侧(r-l+1)就是一个斜率优化的转移形式

考虑如何实现这个斜率优化

首先将(dp_{i,j})两个维护交换,按照分段数一层层进行转移

每一层,可以考虑在(a_i)的笛卡尔树上进行转移

每次考虑所有跨过当前节点的转移区间

那么就要支持:

1.查询左子树(dp_l-l cdot a_u)的最小值

2.更新右子树(dp'_r+rcdot a_u)的最小值


option1

为了实现1操作,容易想到从子树中合并凸包,或者直接进行区间凸包查询

合并凸包的问题可以暴力李超树合并维护

但事实上区间凸包是可以维护的,方法如下

1.维护一个静态线段树,(O(nlog n))归并预处理凸包

2.查询(a_u)递增,具有单调性,可以在每个被查询节点上处理一个指针

复杂度为就是均摊(O(nlog n))


option2

子树更新答案,可以通过可持久化李超树|李超树区间修改实现

复杂度分别为(O(nlog n),O(nlog^2n)),鉴于区间修改常数不满,差别不会太大

但实际上也可以通过朴素凸包实现:

从根开始dfs,每次插入一个(x+y cdot i)的线段形式

(y)是递增的,插入具有单调性,可以维护一个栈凸包

每次二分弹掉节点,插入自己

(不能直接弹,因为要支持回撤,会使得原先是均摊(O(n))的弹栈操作退化为(O(n^2))

查询也可以通过二分解决(这实际上是一个经典树上斜率优化问题)



我当然写了李超树啦

const int N=2e4+10,U=2e4,INF=1e9+10;

int n,m,A[N];
struct Node{
	ll k,b;
	Node(){}
	Node(ll k,ll b):k(k),b(b){}
	ll operator [] (ll x) const { return k*x+b; }
};
struct Tree{
	static const int M=N*20;
	Node s[M];
	int ls[M],rs[M],cnt;
	int New(){
		int u=++cnt;
		ls[u]=rs[u]=0,s[u]=Node(INF,INF);
		return u;
	}
	void Upd(int &p,int l,int r,int ql,int qr,Node x){
		if(x.k==INF) return;
		if(!p) p=New();
		int mid=(l+r)>>1;
		if(ql<=l && r<=qr) {
			if(x[mid]<s[p][mid]) swap(s[p],x);
			if(l==r) return;
			if(x[l]<s[p][l]) Upd(ls[p],l,mid,ql,qr,x);
			if(x[r]<s[p][r]) Upd(rs[p],mid+1,r,ql,qr,x);
			return;
		}
		if(ql<=mid) Upd(ls[p],l,mid,ql,qr,x);
		if(qr>mid) Upd(rs[p],mid+1,r,ql,qr,x);
	}
	ll Que(int p,int l,int r,int x){
		if(!p) return INF;	
		if(l==r) return s[p][x];
		int mid=(l+r)>>1;
		return min(s[p][x],x<=mid?Que(ls[p],l,mid,x):Que(rs[p],mid+1,r,x));
	}
	int Union(int x,int y,int l,int r){
		if(!x||!y) return x|y;
		Upd(x,l,r,l,r,s[y]);
		int mid=(l+r)>>1;
		ls[x]=Union(ls[x],ls[y],l,mid),rs[x]=Union(rs[x],rs[y],mid+1,r);
		return x;
	}
} X,Y;

int ls[N],rs[N],stk[N],top,mk[N];
int dp[110][N];

int rt[N],Rt;
void Solve(int k,int u,int l,int r){
	if(l>r) return;
	Solve(k,ls[u],l,u-1),Solve(k,rs[u],u+1,r);
	rt[u]=rt[ls[u]];
	if(u>1) X.Upd(rt[u],1,U,1,U,Node(-(u-1),dp[k][u-1]));
	int res=X.Que(rt[u],1,U,A[u]);
	Y.Upd(Rt,1,n,u,r,Node(A[u],res));
	rt[u]=X.Union(rt[u],rt[rs[u]],1,U);
}

int main(){
	X.s[0]=Y.s[0]=Node(INF,INF);
	n=rd(),m=rd();
	rep(i,1,n) {
		A[i]=rd();
		while(top && A[stk[top]]<A[i]) ls[i]=stk[top--];
		if(top) rs[stk[top]]=i;
		stk[++top]=i;
	}
	rep(i,1,n) mk[ls[i]]=mk[rs[i]]=1;
	int rt=0;
	rep(i,1,n) if(!mk[i]) rt=i;
	int ma=0;
	rep(i,1,n) cmax(ma,A[i]),dp[1][i]=ma*i;
	rep(i,1,m-1) {
		X.cnt=0,Y.cnt=0;
		Solve(i,rt,1,n);
		rep(j,1,n) dp[i+1][j]=Y.Que(1,1,n,j);
	}
	printf("%d
",dp[m][n]);
}
原文地址:https://www.cnblogs.com/chasedeath/p/14800706.html