#单调队列,平衡树#poj 3017 Cut the Sequence

题目

把一个长度为 n n n的数列分成若干段,使每段的总和不超过 m m m,并且每段的最大值和最小


分析

按照朴素的方法,那么 f [ i ] = m i n 0 ≤ j &lt; i ( ∑ k = j + 1 i A k ≤ m ) { f [ j ] + m a x j &lt; k ≤ i { a [ k ] } } f[i]=min_{0\leq j&lt;i(\sum_{k=j+1}^iA_k\leq m)}\{f[j]+max_{j&lt;k\leq i}\{a[k]\}\} f[i]=min0j<i(k=j+1iAkm){f[j]+maxj<ki{a[k]}},然而可以发现答案需要维护一个单调递减的 A j A_j Aj队列,那问题是答案怎么办,虽然朴素可以水过,但是还是用了平衡树去解决,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)(然而单调队列+朴素,最坏 O ( n 2 ) O(n^2) O(n2)比平衡树快)


代码

#include <cstdio>
#include <set>
typedef long long ll; std::multiset<ll>uk;
int n,a[100005],head,tail,t,q[100005]; 
ll sum,dp[100005],m; bool flag;
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(ll ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int main(){
	n=in(); scanf("%lld",&m);
	t=1; tail=-1;
	for (register int i=1;i<=n&&!flag;i++){
		sum+=(a[i]=in());
		while (sum>m) sum-=a[t++];//找到满足答案的j
		if (t>i) flag=1;//已经不可能了
		while (head<=tail&&a[q[tail]]<=a[i]){//维护队尾
			if (head<tail) uk.erase(dp[q[tail-1]]+a[q[tail]]);
			tail--;
		} 
		q[++tail]=i;//入队
		if (head<tail) uk.insert(dp[q[tail-1]]+a[q[tail]]);
		while (q[head]<t){
			if (head<tail) uk.erase(dp[q[head]]+a[q[head+1]]);
			head++;
		}
		dp[i]=dp[t-1]+a[q[head]];
		if (head<tail&&dp[i]>*(uk.begin())) dp[i]=*(uk.begin());
	}
	if (flag) putchar('-'),putchar('1');
	   else if (dp[n]) print(dp[n]); else putchar('0');
	return !putchar('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值