【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日礼物 堆+链表(模拟费用流)

【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;
}
时间: 2024-10-27 17:13:56

【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日礼物 堆+链表(模拟费用流)的相关文章

POJ 2195 Going Home(网络流-费用流)

Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 17777   Accepted: 9059 Description On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertical

POJ 2677 旅行商问题 双调dp或者费用流

Tour Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3408   Accepted: 1513 Description John Doe, a skilled pilot, enjoys traveling. While on vacation, he rents a small plane and starts visiting beautiful places. To save money, John must

poj 3422 Kaka&#39;s Matrix Travels 费用流

题意: 给一个n*n的矩阵,每次从左上角走到右下角并取走其中的数,求走k次能取到的最大和. 分析: 费用流边的容量有限制的作用,费用有求和的作用,对于每个点只能取一次,容易想到把这个点拆成两个点并连上容量为1,费用为该点数的边.但明显有的流要"跳过"这个点,如何处理呢?可以加一条容量为无穷,费用为0的边,这样不参加这点费用计算的流就可以"跳过"这个点了. 代码: //poj 3422 //sep9 #include <iostream> #include

POJ 2516 Minimum Cost(网络流之费用流)

题目地址:POJ 2516 我晕啊...这题一上来就想到了对每种货物分开求..但是马上就放弃了..感觉这样求50次费用流太耗时..后来就果断拆点,拆了好长时间,一直TLE..即使降到了2600个点也TLE..然后又想起了这个分开求的方法,又突然觉得100个点的费用流几乎不费什么时间..最多也只是求50次而已,还是可以试试的..于是一试居然还真过了... 说到这里,思路应该已经知道了吧.就是对每种货物分开求,因为每种货物是相互独立的.每一次的建图思路就是: 源点与供应商连边,流量权值为供应商这种货

POJ 2135 Farm Tour(网络流之费用流)

题目地址:POJ 2135 来回走一遍可以看成从源点到汇点走两遍.将每个点的流量设为1,就可以保证每条边不重复.然后跑一次费用流就行了.当流量到了2之后停止,输出此时的费用. #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <qu

bzoj3502[PA2012]Tanie Linie(最大k区间和)

题意:给定一个长为n的数列,要求选出最多k个不相交的区间(可以不选),使得选中的数字之和最大.(1<=k<=n<=1000000)分析:首先我们通过预处理对问题做一些简化.原序列中的0对答案没有影响,可以直接删掉.连续的一段正数或一段负数一定是都选或者都不选,可以合并成一个数字.这样把序列转化成了正数和负数交替出现的形式.如果序列的最左端/最右端是负数,这个负数在最优解当中一定不会被选中,我们可以把它删掉.这样就把序列变成了正负交替,以正数开头和结尾的形式.这时若直接考虑选出k个不相交区

[bzoj2288][POJ Challenge]生日礼物

用堆维护双向链表来贪心... 数据范围显然不容许O(nm)的傻逼dp>_<..而且dp光是状态就n*m个了..显然没法优化 大概就会想到贪心乱搞了吧...一开始想贪心地通过几段小的负数把正数连接成一段,但到底是要连接在一起还是直接扔掉不好判断 然后就跑去翻题解了...题解讲的挺好的,连我都看懂了>_<..题解网址:http://www.cnblogs.com/tuigou/p/4868127.html 虽然选正数和负数的意义不同,但实际的操作都是把两边的数合并起来.还有就是,对于在

【BZOJ】3502 PA2012 Tanie linie

[算法] [题解] 胡策k≤10的环状DP做法: 1.钦定法:先确定第一位(可能和第n位)的状态,然后后面正常做DP,显然正确答案是一定会被记录的,因为从整体上看不会有影响. 2.环的特性:取的段和不取的段数量相等,位置互补.所以1和n的连接处都选或都不选都会有不被包括的情况,一选一不选就和链一样了. 正解贪心: 因为相邻的正数或相邻的负数肯定是要选一起选,所以点缩成正负正负-的数列形式,那么考虑先选择全部正的. 如果选择的段数过多,考虑删除则有两种选择:舍弃一个正数(相当于舍弃一个正数和两个负

【UVALive - 5131】Chips Challenge(上下界循环费用流)

Description A prominent microprocessor company has enlisted your help to lay out some interchangeable components(widgets) on some of their computer chips. Each chip’s design is an N × N square of slots. Oneslot can hold a single component, and you ar