CSAcademy Round #41 Add and Subtract

题目链接

link

Sol

process

k k k的最优解一定是由 k − 2 k-2 k2的最优解加上两个元素得到的。

通过线段树维护当前贡献最大的一对元素,可以在 O ( n log ⁡ n ) O(n\log n) O(nlogn)的时间内解决问题。

正确性证明

C k C_{k} Ck k k k的最优解选的 k k k个元素的下标构成的集合 { i 1 , i 2 ⋯ i k } \{ i_1,i_2 \cdots i_k\} {i1,i2ik},设 C k C_k Ck的权值为 w ( C k ) w(C_k) w(Ck)

P C k ( l , r ) = a i l ⋅ sign ( l ) + a i r ⋅ sign ( r ) + 2 ∑ j = l + 1 r − 1 a i j ⋅ sign ( j ) P_{C_k}(l,r) = a_{i_l}\cdot \text{sign}(l) +a_{i_r}\cdot \text{sign}(r) + 2 \sum_{j=l+1}^{r-1}a_{i_j} \cdot \text{sign}(j) PCk(l,r)=ailsign(l)+airsign(r)+2j=l+1r1aijsign(j),其中 sign ( j ) \text{sign}(j) sign(j) i j i_j ij记入 C k C_k Ck的答案的时候乘的系数( 1 1 1 − 1 -1 1)。设 β = min ⁡ { P C k + 2 ( l , r ) } \beta = \min\{P_{C_{k+2}}(l,r)\} β=min{PCk+2(l,r)}

C k = C k + 2 ∖ { l , r } C_{k} = C_{k+2} \setminus\{l,r\} Ck=Ck+2{l,r},其中 l , r l,r l,r使 P C k + 2 P_{C_{k+2}} PCk+2取到最小值。则 w ( C k ) = w ( C k + 2 ) − P C k + 2 ( l , r ) w(C_k)= w(C_{k+2}) - P_{C_{k+2}}(l,r) w(Ck)=w(Ck+2)PCk+2(l,r)

假设存在另一个与 C k C_k Ck不同的 C k ′ C_k' Ck满足 w ( C k ′ ) > w ( C k ) w(C_k')>w(C_k) w(Ck)>w(Ck),可以证明一定可以找到两个下标 x , y ∈ C k + 2 x,y\in C_{k+2} x,yCk+2,使得: x , y ∉ C k ′ x,y\not\in C_{k}' x,yCk,且如果令 C k + 2 ′ = C k ′ ⋃ { x , y } C_{k+2}' = C_k' \bigcup \{ x,y\} Ck+2=Ck{x,y},有 w ( C k + 2 ′ ) = w ( C k ′ ) + P C k + 2 ( x , y ) w(C_{k+2}')=w(C_{k}')+P_{C_{k+2}}(x,y) w(Ck+2)=w(Ck)+PCk+2(x,y)成立。而由于 P C k + 2 ( x , y ) ≥ β P_{C_{k+2}}(x,y)\ge \beta PCk+2(x,y)β,故而 w ( C k + 2 ′ ) > w ( C k + 2 ) w(C_{k+2}') > w(C_{k+2}) w(Ck+2)>w(Ck+2),与已知矛盾。故而 C k = C k + 2 ∖ { l , r } C_k=C_{k+2}\setminus\{l,r\} Ck=Ck+2{l,r}是最优解。

下面归纳证明对于任意的 C k + 2 C_{k+2} Ck+2和任意的 C k ′ C_k' Ck一定可以找到这样的 x , y x,y x,y

1)当 k = 0 k=0 k=0或者 k = 1 k=1 k=1的时候通过分类讨论可以证明命题为真;
2)当 k > 1 k>1 k>1的时候,考虑序列 a a a的所有前缀:

  • 如果存在一个非空前缀,这个前缀中属于 C k ′ C_k' Ck的元素不少于属于 C k + 2 C_{k+2} Ck+2的元素,那么整个序列去掉这个前缀得到的后缀是一个规模更小的问题。
  • 如果存在一个不为整个序列的前缀,这个前缀中属于 C k ′ C_k' Ck的元素比属于 C k + 2 C_{k+2} Ck+2的元素少至少两个,那么这个前缀是一个规模更小的问题。
  • 如果上述两种情况都不存在,则 C k + 2 C_{k+2} Ck+2中的第一个和最后一个元素一定都不属于 C k ′ C_k' Ck,并且 C k ′ C_k' Ck中所有的元素都在它们两个之间,把它们作为 x , y x,y x,y就可以了。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define inf 1000000000000
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1e5+10;
struct item {
	ll pre,suf,mx,sum;
	int pr,sf,l,r;
	item(int x=0,int pos=0): pre(-x),pr(pos),suf(x),sf(pos),mx(-inf),l(0),r(0),sum(0) {}
	void del(int ty) { sum=ty*suf; pre=suf=mx=-inf; }
	friend item operator + (item A,item B) {
		item C; C.sum=A.sum+B.sum;
		if(A.sum+B.pre>A.pre) C.pre=A.sum+B.pre,C.pr=B.pr;
		else C.pre=A.pre,C.pr=A.pr;
		if(B.sum+A.suf>B.suf) C.suf=B.sum+A.suf,C.sf=A.sf;
		else C.suf=B.suf,C.sf=B.sf;
		ll t=A.suf+B.pre;
		if(t>max(A.mx,B.mx)) C.mx=t,C.l=A.sf,C.r=B.pr;
		else if(A.mx>B.mx) C.mx=A.mx,C.l=A.l,C.r=A.r;
		else C.mx=B.mx,C.l=B.l,C.r=B.r;
		return C;
	}
}sum[N<<2][2];
int tg[N<<2];
int A[N],n;
inline void rev(int c) { swap(sum[c][0],sum[c][1]),tg[c]^=1; }
inline void push_up(int c) { sum[c][0]=sum[c<<1][0]+sum[c<<1|1][0],sum[c][1]=sum[c<<1][1]+sum[c<<1|1][1]; }
inline void push_down(int c) { if(tg[c]) rev(c<<1),rev(c<<1|1),tg[c]=0; }
void build(int l,int r,int c) {
	tg[c]=0;
	if(l==r) return (void)(sum[c][0]=item(A[l],l),sum[c][1]=item(-A[l],l));
	int mid=l+r>>1; build(l,mid,c<<1),build(mid+1,r,c<<1|1); push_up(c);
}
int qv,qt,ql,qr;
void upd(int l,int r,int c) {
	if(ql<=l&&qr>=r) return rev(c);
	int mid=l+r>>1; push_down(c);
	if(ql<=mid) upd(l,mid,c<<1);
	if(qr>mid) upd(mid+1,r,c<<1|1);
	push_up(c);
}
void del(int l,int r,int c) {
	if(l==r) return sum[c][0].del(qt),sum[c][1].del(qt);
	int  mid=l+r>>1; push_down(c);
	if(qv<=mid) del(l,mid,c<<1);
	else del(mid+1,r,c<<1|1);
	push_up(c);
}
ll Ans[N];
void sol(int flg) {
	build(1,n,1);
	ll ans=0; int tot=0;
	if(flg) {
		int p=1; for(int i=2;i<=n;++i) if(A[i]>A[p]) p=i;
		qv=p,qt=-2,del(1,n,1);
		if(p<n) ql=p+1,qr=n,upd(1,n,1);
		ans+=A[p],tot++;
	}
	Ans[tot]=ans;
	for(;tot+2<=n;tot+=2) {
		int l=sum[1][0].l,r=sum[1][0].r; ans+=sum[1][0].mx;
		qv=l,qt=-2,del(1,n,1); qv=r,qt=2,del(1,n,1);
		if(l+1<r) ql=l+1,qr=r-1,upd(1,n,1);
		Ans[tot+2]=ans;
	}
}
int main() {
	rd(n);
	for(int i=1;i<=n;++i) rd(A[i]);
	sol(0),sol(1);
	for(int i=1;i<=n;++i) printf("%lld ",Ans[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值