【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 線段樹優化建圖+費用流
阿新 • • 發佈:2017-07-04
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 線段樹優化建圖+費用流