把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【LOJ2840】「JOISC 2018 Day 4」糖(模拟费用流)

点此看题面

  • 给定一个长度为\(n\)的序列,对于所有\(i=1\sim\lceil\frac n2\rceil\),求出选择\(i\)个不相邻元素的最大和。
  • \(n\le2\times10^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('\n');}
}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;
}
posted @ 2021-06-03 14:07  TheLostWeak  阅读(237)  评论(0编辑  收藏  举报