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