1. 程式人生 > >【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日禮物 堆+鏈表(模擬費用流)

【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日禮物 堆+鏈表(模擬費用流)

line make 前驅 string urn return namespace 禮物 iostream

【BZOJ3502】PA2012 Tanie linie

Description

n個數字,求不相交的總和最大的最多k個連續子序列。
1<= k<= N<= 1000000。

Sample Input

5 2
7 -3 4 -9 5

Sample Output

13

題解:跟1150和2151差不多。

我們先做一些預處理,因為連續的正數和連續的負數一定是要麽都選要麽都不選,所以可以將它們合並成一個數,同時區間中的零以及左右兩端的負數沒有意義,可以將它們刪掉。然後我們得到的序列就變成:正-負-正-...-負-正。

然後我們貪心的把所有正數都選了,如果正數的部分<=k,那麽直接取光即可,否則,我們還要將我們的子串個數減少一些。如何減少呢?1:將某個正數由選變為不選。2.將某個負數由不選變為選。我們發現,這兩種情況都會使答案減少:那個數的絕對值,所以我們可以用堆維護所有數的絕對值,然後每次都貪心的選取絕對值小的。

但是直接貪心肯定不行,我們要模擬費用流的過程,也就是加入一個反悔操作。發現:如果對一個整數進行了1操作,那麽我們就無法對它相鄰的負數進行2操作;如果對一個負數進行了2操作,那麽我們就無法對它相鄰的正數進行1操作。所以我們再堆中刪掉與它相鄰的點。那麽怎麽反悔呢?如果當前是b,b的前驅是a,後繼是c,那麽向堆中加入a+c-b。如果再次選擇了a+c-b,意味著我們撤銷了對b的操作,而是對a和c進行操作,這樣就和費用流是一樣的了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <utility>
#include <algorithm>
#define mp(A,B) make_pair(A,B)
using namespace std;
typedef long long ll;
const int maxn=1000010;
int n,m,k;
ll ans;
ll v[maxn],p[maxn];
int pre[maxn],nxt[maxn],del[maxn];
typedef pair<ll,int> pli;
priority_queue<pli> q;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,a,b,tp=0;
	for(i=1;i<=n;i++)
	{
		v[i]=rd();
		if(v[i]>0)
		{
			ans+=v[i];
			if(p[tp]>0)	p[tp]+=v[i];
			else	p[++tp]=v[i];
		}
		if(v[i]<0)
		{
			if(p[tp]<=0)	p[tp]+=v[i];
			else	p[++tp]=v[i];
		}
	}
	if(p[tp]<=0)	tp--;
	n=tp;
	if((n+1)>>1<=m)
	{
		printf("%lld\n",ans);
		return 0;
	}
	m=((n+1)>>1)-m;
	for(i=1;i<=n;i++)	p[i]=abs(p[i]),q.push(mp(-p[i],i)),pre[i]=i-1,nxt[i]=(i<n)?(i+1):0;
	while(m--)
	{
		while(del[q.top().second])	q.pop();
		ans+=q.top().first,i=q.top().second,q.pop();
		a=pre[i],b=nxt[i],del[a]=del[b]=1;
		if(a&&b)
		{
			pre[i]=pre[a],nxt[i]=nxt[b];
			if(pre[i])	nxt[pre[i]]=i;
			if(nxt[i])	pre[nxt[i]]=i;
			p[i]=p[a]+p[b]-p[i],q.push(mp(-p[i],i));
		}
		else
		{
			pre[i]=pre[a],nxt[i]=nxt[b];
			if(pre[i])	nxt[pre[i]]=nxt[i];
			if(nxt[i])	pre[nxt[i]]=pre[i];
			del[i]=1;
		}
	}
	printf("%lld",ans);
	return 0;
}

【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日禮物 堆+鏈表(模擬費用流)