【bzoj4276】[ONTAK2015]Bajtman i Okrągły Robin 线段树优化建图+费用流

题目描述

有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元。作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢?

输入

第一行包含一个正整数n(1<=n<=5000),表示强盗的个数。
接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]<=10000),依次描述每一个强盗。

输出

输出一个整数,即可以挽回的损失的最大值。

样例输入

4
1 4 40
2 4 10
2 3 30
1 3 20

样例输出

90


题目大意

有n个强盗,其中第i个强盗会在[a[i],b[i]]这段时间内进行抢劫,并计划抢走c[i]元。作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢?

题解

线段树优化建图+费用流(+语文题)

想要做这道题,必须先读懂题(个人觉得题目描述有问题,所以重新翻译了一遍)

读懂题以后发现是费用流,先将b[i]--,把时间段转化为点;然后连边S->强盗,容量为1,费用为c[i];强盗->a[i]、a[i]+1、...、b[i],容量为1,费用为0;时间k->T,容量为1,费用为0。跑最大费用最大流即为答案。

但是这样边数过多,考虑到时间是一段连续的区间,所以可以使用线段树优化建图。

建立线段树,从父节点向子节点连容量为inf,费用为0的边;S->强盗,容量为1,费用为c[i];对于每个强盗,找到[a[i],b[i]]对应的区间,该强盗向这些区间连边,容量为1,费用为0;叶子结点向T连边,容量为1,费用为0。这张图的最大费用最大流即为答案。

另外此题的spfa费用流必须加inq(在队列中)优化,否则会TLE。

#include <cstdio>
#include <cstring>
#include <queue>
#define N 30000
#define M 3000000
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
const int inf = 1 << 30;
queue<int> q;
int a[N] , b[N] , c[N] , head[N] , to[M] , val[M] , cost[M] , next[M] , cnt = 1 , s , t , dis[N] , from[N] , pre[N] , inq[N];
inline int read()
{
	int ret = 0; char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = getchar();
	return ret;
}
void add(int x , int y , int v , int c)
{
	to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt;
}
bool spfa()
{
	int x , i;
	memset(from , -1 , sizeof(from));
	memset(dis , 0x3f , sizeof(dis));
	dis[s] = 0 , q.push(s);
	while(!q.empty())
	{
		x = q.front() , q.pop() , inq[x] = 0;
		for(i = head[x] ; i ; i = next[i])
		{
			if(val[i] && dis[to[i]] > dis[x] + cost[i])
			{
				dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i;
				if(!inq[to[i]]) inq[to[i]] = 1 , q.push(to[i]);
			}
		}
	}
	return ~from[t];
}
int mincost()
{
	int ans = 0 , i , k;
	while(spfa())
	{
		k = inf;
		for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]);
		ans += k * dis[t];
		for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k;
	}
	return ans;
}
void build(int l , int r , int x)
{
	if(l == r)
	{
		add(x , t , 1 , 0);
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	add(x , x << 1 , inf , 0) , add(x , x << 1 | 1 , inf , 0);
}
void update(int b , int e , int k , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		add(k , x , 1 , 0);
		return;
	}
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , k , lson);
	if(e > mid) update(b , e , k , rson);
}
int main()
{
	int n , m = 0 , i;
	n = read();
	for(i = 1 ; i <= n ; i ++ ) a[i] = read() , b[i] = read() - 1 , c[i] = read() , m = max(m , b[i]);
	s = 0 , t = 4 * m , build(1 , m , 1);
	for(i = 1 ; i <= n ; i ++ ) add(s , t + i , 1 , -c[i]) , update(a[i] , b[i] , t + i , 1 , m , 1);
	printf("%d\n" , -mincost());
	return 0;
}

 

 

posted @ 2017-07-04 09:59  GXZlegend  阅读(346)  评论(0编辑  收藏  举报