• HDU多校第三场 Hdu6606 Distribution of books 线段树优化DP


    Hdu6606 Distribution of books

    题意

    把一段连续的数字分成k段,不能有空段且段和段之间不能有间隔,但是可以舍去一部分后缀数字,求(min(max((sum ai )))其中(sum ai)为一段的数字和

    分析

    最小化最大值问题通常我们要想到二分,所以答案的求法我们就解决了,但是二分我们怎么check呢?这个时候一点思路都没有,我们考虑暴力的算法,设dp[i]表示从1--i最多可以分成多少段,怎么转移,什么情况下可以转移呢?
    显然(dp[i]=max(dp[j]+[sum[i]-sum[j]<=x]))这样就能取到最多段了,但是这个转移的写法是(O(n^2))的暴力算法,显然不满足题目的复杂度,这就到了这个题目的难点了,如何对dp进行优化,对dp进行优化的方式有多种,单调栈、平行四边形、线段树、主席树等。
    关于dp优化:https://blog.csdn.net/qq_35649707/article/details/77834685
    这里因为取值满足不等式,而不等式所表示的值域就是天然的区间,区间问题肯定非线段树莫属。我们采用线段树优化,使用权值线段树的节点对应离散化后的sum值,从而维护dp的值,由不等(sum[i]-sum[j]<=x)我们转化为(sum[i]-x<=sum[j])即只要找第一个满足该不等式的sum,然后在([j,m])中找最大的dp值即可转移,复杂度就优化到了(O(nlognlogn)=O(nlog^2n))

    #include<bits/stdc++.h>
    #define pb push_back
    #define F first
    #define S second
    #define pii pair<int,int>
    #define mkp make_pair
    typedef long long ll;
    using namespace std;
    const int maxn=2e5+5;
    int tr[maxn<<2];
    int a[maxn];
    ll sum[maxn],v[maxn];
    int t,n,k,flag,cnt=0,sz;
    void build(int o,int l,int r){
    	tr[o]=-maxn;
    	if(l==r)return ;
    	int mid=l+r>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    }
    void update(int o,int l,int r,int p,int v){
    	if(l==r)tr[o]=max(tr[o],v);
    	else {
    		int mid=l+r>>1;
    		if(mid>=p)update(o<<1,l,mid,p,v);
    		else update(o<<1|1,mid+1,r,p,v);
    		tr[o]=max(tr[o<<1],tr[o<<1|1]);
    	}
    }
    int query(int o,int l,int r ,int x,int y){
    	if(x<=l&&y>=r)return tr[o];
    	int mid=l+r>>1;
    	int ans=-maxn;
    	if(mid>=x)ans=max(ans,query(o<<1,l,mid,x,y));
    	if(mid<y)ans=max(ans,query(o<<1|1,mid+1,r,x,y));
    	return ans;
    }
    bool  check(ll x){
    	build(1,1,sz);
    	for(int i=1;i<=n;i++){
    		int l=lower_bound(v+1,v+1+sz,sum[i]-x)-v;
    		int id=lower_bound(v+1,v+1+sz,sum[i])-v;
    		//cout<<" i "<<i<<" l "<<l<<" id "<<id<<" sum-x "<<sum[i]-x<<endl;
    		if(l>sz){
    			if(sum[i]<=x)
    			update(1,1,sz,id,1);
    		}
    		else {
    			int t=query(1,1,sz,l,sz);
    			//cout<<i<<" t "<<t<<endl;
    			if(t==-maxn){
    				if(sum[i]<=x)
    				update(1,1,sz,id,1);
    			}
    			else 
    			update(1,1,sz,id,t+1);
    		}
    	}
    	//cout<<query(1,1,sz,1,sz)<<endl;
    	return query(1,1,sz,1,sz)>=k;
    }
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		cnt=1;
    		scanf("%d%d",&n,&k);
    		for(int i=1;i<=n;i++){
    			scanf("%d",&a[i]);
    			sum[i]=sum[i-1]+a[i];
    			v[cnt++]=(sum[i]);
    		}
    		cnt--;
    		sort(v+1,1+v+cnt);
    		sz=unique(v+1,v+1+cnt)-v-1;
    	//	for(int i=1;i<=sz;i++)cout<<v[i]<<" ";
    	//	cout<<endl;
    		//cout<<sz<<endl;
    		ll l=-1e14;
    		ll r=-l;
    		ll ans=1e15;
    		while(l<=r){
    			ll mid=l+r>>1;
    			//cout<<l<<" "<<r<<endl;
    			if(check(mid)){
    				ans=min(ans,mid);
    				r=mid-1;
    			}
    			else l=mid+1;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    彻底理解 Python 生成器
    Windows上虚拟环境的安装及使用
    github怎么绑定自己的域名
    解决ImportError: cannot import name HTTPSHandler
    服务器(Linux) 安装python3
    函数的参数(必选,默认,可变,关键字)
    python 异常处理(try...finally...和with...as 方法)
    LeetCode 33. 搜索旋转排序数组 | Python
    LeetCode 46. 全排列
    LeetCode 面试题51. 数组中的逆序对
  • 原文地址:https://www.cnblogs.com/ttttttttrx/p/11310867.html
Copyright © 2020-2023  润新知