zoukankan      html  css  js  c++  java
  • 「题解」300iq Contest 2 H. Honorable Mention

    本文将同步发布于:

    题目

    题目链接:gym102331H

    题意概述

    给定一个长度为 (n) 的序列 (a),有 (q) 次询问,每次询问给定三个参数 (l,r,k),求出对于区间 ([l,r]),你将其划分为若干个子区间,然后取其中的 (k) 个,最大化取出来的所有元素的和。即:最大 (k) 子段和。

    (1leq n,qleq 3.5 imes 10^4)(|a_i|leq 3.5 imes 10^4)

    题解

    寻找函数的性质

    如果我们设 (f_{[l,r]}(k)) 表示区间 (l,r) 的最大 (k) 子段和,那么我们不难猜测到 (l,r) 相同时,(f(k)) 是一个上凸函数。

    我们考虑证明这一点,即 (f(k)-f(k-1)geq f(k+1)-f(k))

    考虑反证法,设 (exists xinmathbb{N}),满足 (f(x)-f(x-1)<f(x+1)-f(x))

    那么我们考虑在 (x-1,x,x+1) 的时候的选取方案。

    • 若取 (x+1) 的时候正子段仍未取完,那么 (f(x))(f(x-1)) 多取了一个区间 (p(p>0))(f(x+1))(f(x)) 多取了一个区间 (q(q>0))
      如果我们认为 (f(x)-f(x-1)<f(x+1)-f(x)),也就是 (p<q),那么我们不如交换这两个区间,可以使得 (f(x)) 更优。
    • 若取 (x-1) 的时候正子段取完了,那么 (f(x))(f(x-1)) 多取了一个区间 (p(p<0))(f(x+1))(f(x)) 多取了一个区间 (q(q<0))
      如果我们认为 (f(x)-f(x-1)<f(x+1)-f(x)),也就是 (p<q),那么我们不如交换这两个区间,可以使得 (f(x)) 更优。

    综上所述,如果选取的方案不满足上凸包的性质,我们总是可以通过调整法将其变成上凸包。

    合并凸包——闵可夫斯基和

    如果我们求出了区间 ([l,r]) 内的 (f(k)),我们就想要知道这个东西是否支持快速合并,例如 (f_{[l, exttt{mid}]}+f_{[ exttt{mid}+1,r]} o f_{[l,r]})

    答案是可以的。

    考虑到 (f(k)) 的凸性,我们不妨使用 闵可夫斯基和 对两个凸包进行合并,时间复杂度为 (Theta(r-l))

    简单做法

    通过上面的叙述,我们已经得到了一个简单的做法。

    每次询问时,我们在线段树上求出此次询问覆盖的区间,并将所有的凸包合并,然后直接得到 (f(k)) 即为答案。

    考虑分析时间复杂度,不难发现,这种做法的单次询问时间复杂度与区间长度有关,我们需要更优秀的做法。

    wqs 二分

    我们考虑不将区间合并,而是直接在线段树上的 (Theta(log_2n)) 个区间内求解答案。

    具体地,我们决定使用 wqs 二分,解除掉选择区间个数的限制,然后各个区间就可以互不干扰的选择,很容易就能求出最优解。通过调整最终的斜率,我们可以得出答案,时间复杂度为 (Theta(qlog^3_2n))

    整体 wqs 二分

    我们考虑到,当 (k) 增大时,其对应的 wqs 二分时的斜率也会越大,因此这个二分具有单调性,我们可以将询问对 (k) 排序,然后进行整体二分,常数更小的方法是在线段树上维护一个指针,表示上一次 (kleq x) 的最优位置,然后暴力自增即可。

    时间复杂度为 (Theta(qlog_2^2n))

    参考程序

    #include<bits/stdc++.h>
    using namespace std;
    #define reg register
    typedef long long ll;
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    static char buf[100000],*p1=buf,*p2=buf;
    #define flush() (fwrite(wbuf,1,wp1,stdout),wp1=0)
    #define putchar(c) (wp1==wp2&&(flush(),0),wbuf[wp1++]=c)
    static char wbuf[1<<21];int wp1;const int wp2=1<<21;
    inline int read(void){
    	reg bool f=false;
    	reg char ch=getchar();
    	reg int res=0;
    	while(!isdigit(ch))f|=(ch=='-'),ch=getchar();
    	while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
    	return f?-res:res;
    }
    
    inline void writeln(reg int x){
    	static char buf[32];
    	reg int p=-1;
    	if(x<0) x=-x,putchar('-');
    	if(!x) putchar('0');
    	else while(x) buf[++p]=(x%10)^'0',x/=10;
    	while(~p) putchar(buf[p--]);
    	putchar('
    ');
    	return;
    }
    
    inline int max(reg int a,reg int b){
    	return a>b?a:b;
    }
    
    const int MAXN=5e4+5;
    const int MAXQ=5e4+5;
    const ll inf=1e12;
    
    struct querys{
    	int id,l,r,k,lef,rig;
    };
    
    inline bool cmp(const querys& a,const querys& b){
    	return (a.lef+a.rig)>(b.lef+b.rig);
    }
    
    typedef vector<ll> Data;
    
    inline Data max(Data a,Data b){
    	if(a.size()>b.size()){
    		for(reg int i=0,siz=b.size();i<siz;++i)
    			a[i]=max(a[i],b[i]);
    		return a;
    	}
    	else{
    		for(reg int i=0,siz=a.size();i<siz;++i)
    			b[i]=max(b[i],a[i]);
    		return b;
    	}
    }
    
    inline Data operator+(Data a,Data b){
    	if(!a.size()||!b.size())
    		return vector<ll>{};
    	Data res;
    	res.resize(a.size()+b.size()-1);
    	res[0]=a[0]+b[0];
    	reg unsigned int i=1,j=1,k=1;
    	while(i<a.size()&&j<b.size())
    		if(a[i]-a[i-1]>b[j]-b[j-1])
    			res[k++]=b[j-1]+a[i++];
    		else
    			res[k++]=a[i-1]+b[j++];
    	while(i<a.size())
    		res[k++]=b[j-1]+a[i++];
    	while(j<b.size())
    		res[k++]=a[i-1]+b[j++];
    	return res;
    }
    
    inline Data shift(Data a){
    	if(!a.size())
    		return vector<ll>{};
    	else
    		return vector<ll>(a.begin()+1,a.end());
    }
    
    inline void print(Data p){
    	for(auto x:p)
    		printf("%lld ",x);
    	return;
    }
    
    int tim;
    pair<ll,int> f[2],g[2];
    
    namespace SegmentTree{
    	#define lson ( (k) << 1 )
    	#define rson ( (k) << 1 | 1 )
    	#define mid ( ( (l) + (r) ) >> 1 )
    	struct Node{
    		Data dat[2][2];
    		int t;
    		unsigned int ptr[2][2];
    		#define dat(x) unit[(x)].dat
    		#define t(x) unit[(x)].t
    		#define ptr(x) unit[(x)].ptr
    	};
    	Node unit[MAXN<<2];
    	inline void pushup(reg int k){
    		dat(k)[0][0]=max(
    			max(dat(lson)[0][0]+dat(rson)[0][0],dat(lson)[0][0]+dat(rson)[1][0]),
    			max(dat(lson)[0][1]+dat(rson)[0][0],max(dat(lson)[0][1]+dat(rson)[1][0],shift(dat(lson)[0][1]+dat(rson)[1][0])))
    		);
    		dat(k)[0][1]=max(
    			max(dat(lson)[0][0]+dat(rson)[0][1],dat(lson)[0][0]+dat(rson)[1][1]),
    			max(dat(lson)[0][1]+dat(rson)[0][1],max(dat(lson)[0][1]+dat(rson)[1][1],shift(dat(lson)[0][1]+dat(rson)[1][1])))
    		);
    		dat(k)[1][0]=max(
    			max(dat(lson)[1][0]+dat(rson)[0][0],dat(lson)[1][0]+dat(rson)[1][0]),
    			max(dat(lson)[1][1]+dat(rson)[0][0],max(dat(lson)[1][1]+dat(rson)[1][0],shift(dat(lson)[1][1]+dat(rson)[1][0])))
    		);
    		dat(k)[1][1]=max(
    			max(dat(lson)[1][0]+dat(rson)[0][1],dat(lson)[1][0]+dat(rson)[1][1]),
    			max(dat(lson)[1][1]+dat(rson)[0][1],max(dat(lson)[1][1]+dat(rson)[1][1],shift(dat(lson)[1][1]+dat(rson)[1][1])))
    		);
    		return;
    	}
    	inline void build(reg int k,reg int l,reg int r,reg int a[]){
    		if(l==r){
    			dat(k)[0][0]=vector<ll>{0,-inf},dat(k)[1][1]=vector<ll>{-inf,a[l]};
    			return;
    		}
    		build(lson,l,mid,a),build(rson,mid+1,r,a);
    		pushup(k);
    		return;
    	}
    	inline void query(reg int k,reg int l,reg int r,reg int L,reg int R,reg ll K){
    		if(t(k)!=tim){
    			t(k)=tim;
    			ptr(k)[0][0]=ptr(k)[0][1]=ptr(k)[1][0]=ptr(k)[1][1]=0;
    		}
    		if(L<=l&&r<=R){
    			g[0]=f[0],g[1]=f[1],f[0]=f[1]=make_pair(-inf,0);
    			for(reg int i=0;i<2;++i)
    				for(reg int j=0;j<2;++j)
    					if(dat(k)[i][j].size()){
    						while(ptr(k)[i][j]<dat(k)[i][j].size()-1&&dat(k)[i][j][ptr(k)[i][j]+1]-dat(k)[i][j][ptr(k)[i][j]]>=K)
    							++ptr(k)[i][j];
    						pair<ll,int> p=g[1],v;
    						if(i&&K>0)
    							p.first+=K,--p.second;
    						v=max(g[0],p);
    						v.first+=dat(k)[i][j][ptr(k)[i][j]]-ptr(k)[i][j]*K,v.second+=ptr(k)[i][j];
    						f[j]=max(f[j],v);
    					}
    			return;
    		}
    		if(L<=mid)
    			query(lson,l,mid,L,R,K);
    		if(R>mid)
    			query(rson,mid+1,r,L,R,K);
    		return;
    	}
    	#undef lson
    	#undef rson
    	#undef mid
    	#undef dat
    	#undef t
    	#undef ptr
    }
    
    int n,q;
    int a[MAXN];
    querys qu[MAXQ],lef[MAXQ],rig[MAXQ];
    int ans[MAXQ];
    
    int main(void){
    	n=read(),q=read();
    	for(reg int i=1;i<=n;++i)
    		a[i]=read();
    	SegmentTree::build(1,1,n,a);
    	for(reg int i=1;i<=q;++i)
    		qu[i].id=i,qu[i].l=read(),qu[i].r=read(),qu[i].k=read(),qu[i].lef=-1e9,qu[i].rig=1e9;
    	while(true){
    		reg int cnt=0;
    		++tim;
    		sort(qu+1,qu+q+1,cmp);
    		for(reg int i=1;i<=q;++i)
    			if(qu[i].lef<=qu[i].rig){
    				++cnt;
    				f[0]=make_pair(0,0),f[1]=make_pair(-inf,0);
    				SegmentTree::query(1,1,n,qu[i].l,qu[i].r,(qu[i].lef+qu[i].rig)>>1);
    				pair<ll,int> res=max(f[0],f[1]);
    				if(res.second>=qu[i].k)
    					ans[qu[i].id]=res.first+qu[i].k*((qu[i].lef+qu[i].rig)>>1),qu[i].lef=((qu[i].lef+qu[i].rig)>>1)+1;
    				else
    					qu[i].rig=((qu[i].lef+qu[i].rig)>>1)-1;
    			}
    		if(!cnt)
    			break;
    	}
    	for(reg int i=1;i<=q;++i)
    		writeln(ans[i]);
    	flush();
    	return 0;
    }
    
  • 相关阅读:
    jQuery.extend()
    reconnecting-websocket.js
    网页防止嵌套
    mysql 一张表的多个字段关联另外一张表
    php把一些预定义的 HTML 实体转换为字符。
    weex用阿里矢量图
    nodeJS有多快
    关于jquery中prev()和next()的用法
    关于手动添加属性的方法总结
    关于一个div上下左右居中的css方法
  • 原文地址:https://www.cnblogs.com/Lu-Anlai/p/14844265.html
Copyright © 2011-2022 走看看