1. 程式人生 > >【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 線段樹優化建圖+費用流

【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 線段樹優化建圖+費用流

har brush while inf uil mes queue eof div

題目描述

有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;
}

【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 線段樹優化建圖+費用流