- 给定一个长度为(n)的序列,对于所有(i=1simlceilfrac n2 ceil),求出选择(i)个不相邻元素的最大和。
- (nle2 imes10^5)
模拟费用流
考虑从小到大枚举枚举选择元素个数,发现其实只有两种增加一个元素的方式:
- 选择一个两侧都未被选择的元素。
- 对于一个段(0101..1010)((1)表示选择),我们将其反转,选择(1010...0101)。
这就是一个模拟费用流的过程(本质是带悔贪心)。
可以把第二种情况当成一个大元素,权值就是所有未选中的元素和减去所有选中的元素和,和第一种情况的所有点一起扔入一个堆里。
每次取出权值最大的元素,分别考虑与它相邻的两个元素:
- 如果它原本就与一个选中元素相邻,就将两段合并。
- 如果它原本不与选中元素相邻,那么只是简单地向这个方向扩展一位。
注意,对于一个选择了(1)或(n)的段,我们不能再放入堆中,因为将它取反不能增加一个元素。
代码:(O(n))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define LL long long
using namespace std;
int n,g[N+5],L[N+5],R[N+5];LL V[N+5];struct Data
{
int p;LL v;I Data(CI x=0,Con LL& y=0):p(x),v(y){}
I bool operator < (Con Data& o) Con {return v^o.v?v<o.v:p<o.p;}//按权值排序
};set<Data> S;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[30],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
');}
}using namespace FastIO;
int main()
{
RI i,j,l,r,x,y;for(read(n),i=1;i<=n;++i) read(V[i]),L[i]=R[i]=i,S.insert(Data(i,V[i]));//初始每个位置都属于第一种情况
LL t=0;Data k;for(i=1;i<=(n+1>>1);++i)
{
S.erase(k=*--S.end()),writeln(t+=k.v),r=R[l=k.p],g[l]=1;
if(l==1) V[x=1]*=-1;else if(g[l-1]) x=L[l-1],S.erase(Data(x,V[x])),V[x]-=V[l];//在左边界;两段合并
else x=l-1,S.erase(Data(l-1,V[l-1])),V[l-1]-=V[l],g[l-1]=1;//简单扩展一位
if(r==n) y=n;else if(g[r+1]) y=R[r+1],S.erase(Data(r+1,V[r+1])),V[x]+=V[r+1];//在右边界;两段合并
else y=r+1,S.erase(Data(r+1,V[r+1])),V[x]+=V[r+1],g[r+1]=1;//简单扩展一位
if(R[L[y]=x]=y,(x^1||(x-l)&1)&&(y^n||(y-r)&1)) S.insert(Data(x,V[x]));//更新左右端点关系,没有选择1或n时才加入堆
}return clear(),0;
}