SPOJ1748 - SEQPAR2 二分答案 DP优化 单调队列+线段树

11 篇文章 0 订阅
8 篇文章 0 订阅

 题目太蛋疼。。

首先有一个N^2LIMIT的最最暴力的DP。囧


然后可以换个DP的东西,二分答案,然后DP验证是否可行。(f[n]<=LIMIT)

对于第一个条件,其实可以与处理出来最小的必须在一起的块。然后合并起来。然后第一个条件就没了。

然后就要考虑第二个条件了。

还是二分答案,DP验证 。但是N^2的暴力DP肯定要TLE的。

考虑优化一下dp.

转移是:

f[i]=Min{f[j]+MaxA(j+1,i)}  sumb(j+1,i)≤X (x是当前二分的答案)


然后这玩意儿竟然可以优化。

MaxA(j+1,i)是随j变大单调不增的。

然后考虑以A为关键字建一个单调递减的栈。

然后用线段树维护f[j]+MaxA(j+1,i) 整个的值

但是修改的只是MaxA(j+1,i)

MaxA(j+1,i)是成段变化的,每次加上一个差值就可以了。

但是f[j]+MaxA(j+1,i) 是不单调的。所以要在线段树里面询问最小值

然后修改一段区间是logN的 ,每个点最多单独更新一次。每次DP是NlogN的。


下面是错误的代码。。。(为啥还放上来 。。)

#include <cstdio>
#include <algorithm>
#define per(i,r,l) for (int i=r;i>=l;--i)
#define rep(i,l,r) for (int i=l;i<=r;++i)
typedef unsigned int UI;
bool upmax(int &a,int b){return a<b?a=b,1:0;}
bool upmax(UI &a,UI b){return a<b?a=b,1:0;}
UI Max(UI a,UI b){return a<b?b:a;}
const int MAX_N=50050;
UI n,lim;
UI a[MAX_N],b[MAX_N];
UI sum[MAX_N];
void New(){
	static UI max[MAX_N];
	per(i,n,1) max[n-i+1]=Max(max[n-i],a[i]);
	static int L[MAX_N],R[MAX_N],top=0;
	bool fir=true;int r=0;
	rep(i,1,n){
		upmax(r,n-(std::lower_bound(max,max+n-i+1,b[i])-max)+1);
		if (fir) L[++top]=i,fir=0;
		if (i==r) R[top]=i,fir=1;
		}
	n=top;
	rep(i,1,top){
		UI s=0,mx=0;
		rep(j,L[i],R[i]) upmax(mx,a[j]),s+=b[j];
		a[i]=mx,b[i]=s;
		sum[i]=sum[i-1]+b[i];
		}
}
int getp(UI *a,int l,int r,int v){
	if (v<a[l]) return l;
	while (r-l>1){
		int mid=l+r>>1;
		if (v>a[mid]) r=mid;
			else l=mid;
		}
	return r;
}
bool check(UI x){
	static UI f[MAX_N],que1[MAX_N],que2[MAX_N];
	int h1=0,t2=0,h2=0;
	que2[t2]=0;f[0]=0;
	rep(i,1,n){
		while(0<h1&&a[que1[h1]]<=a[i]) h1--;
		que1[++h1]=i;
		while(t2<h2&&((sum[i]-sum[que2[t2]])>x)) t2++;
		f[i]=f[que2[t2]]+a[getp(que1,1,h1,que2[h2])];
		//printf("%d\n",f[i]);
		while(t2<h2&&(   f[que2[h2]]+a[getp(que1,1,h1,que2[h2])]  
						>f[i]   +    a[getp(que1,1,h1,i)])) h2--;
		que2[++h2]=i;
		}
	return f[n]<=lim;
}
void Solve(){
	UI l=0,r=1;
	rep(i,1,n) upmax(l,b[i]-1),r+=b[i];
	while (r-l>1){
		UI mid=l+r>>1;
		if (check(mid)) r=mid;
			else l=mid;
		}
	printf("%d\n",r);
}
int main(){
	freopen("partition.in","r",stdin);
	freopen("partition.out","w",stdout);
	scanf("%u%u",&n,&lim);
	rep(i,1,n) scanf("%u%u",&a[i],&b[i]);
	New();
	//rep(i,1,n) printf("%d %d\n",a[i],b[i]);
	//check(9);
	Solve();
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]中的描述,p1168问题使用了线段解法。在构造的过程中,需要遍历整棵,所以时间复杂度为O(n)。但是在改变一个元素的值时,时间复杂度只有O(log(n))。求和的时候,的节点表示一个索引范围内元素值之和,只要将区间分割对应上,平均时间复杂度是O(log(n)),最坏情况下不会超过O(n*log(n))。\[1\] 根据引用\[2\]中的描述,QUERY 5 12应该是第4、5条边的极大值。\[2\] 根据引用\[3\]中的描述,代码中的if(L<=MID)和else if(R>MID)的判断条件是为了确保查询范围在左子或右子中。如果加上else,会导致错误。\[3\] #### 引用[.reference_title] - *1* [leetCode307:线段解法](https://blog.csdn.net/cyd1999/article/details/123963164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spoj 375 Qtree 链剖分 + 线段 解法](https://blog.csdn.net/niuox/article/details/8145842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值