题目链接
Sol
process
k k k的最优解一定是由 k − 2 k-2 k−2的最优解加上两个元素得到的。
通过线段树维护当前贡献最大的一对元素,可以在 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,i2⋯ik},设 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)=ail⋅sign(l)+air⋅sign(r)+2∑j=l+1r−1aij⋅sign(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,y∈Ck+2,使得: x , y ∉ C k ′ x,y\not\in C_{k}' x,y∈Ck′,且如果令 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;
}