ZOJ3496 Assignment
阿新 • • 發佈:2018-12-18
這題也是真噁心……
題目大意是倆公司要運貨,每條路有容量上限。然後B公司手裡有p個……(技能點?)如果在一條路上放了x個技能點,這條路經過了y個貨物,那麼B公司就會收x*y的錢。現在要求的是,B公司最大獲利和A公司在付費最大時的最小值,和這個問題完全倒過來。
首先有一個結論是B公司一定會把所有技能點一股腦全放在流量最大的那條邊上。(貪心顯然)注意是當前圖上的流量而不是原圖容量。那麼這個問題就轉化成了有上/下界限制的最大最小流。對於第一個問題,我們二分一個流的上界,首先判斷這個其實就相當於一個正常的網路流,直接跑dinic即可,然後記錄一下是否與一開始跑出來的值是相等的,這樣二分求最大值即可。(答案就是當前容量上限的最大值*p)
而對於第二個問題,我們二分流的下界。這個時候就是一個有上下界的網路流,要建立輔助源匯點那一套跑流。不過具體的二分操作依然沒變。注意的是,如果在二分過程中,當前的下屆大於原來的上界,就直接返回0.最後我們求出最小的下界,然後用它乘以p來更新答案。
但是這題坑點很多的……首先是因為這題重構圖很多,注意不能大量用memset。還有就是在head複製的時候不要複製少了,輔助源匯點編號不要小了(因為這題S,T)是給定的……,還有就是這次在判斷最小流的時候……刪除輔助源匯點最穩妥的做法是把其head設為-1,這樣可以保證不會跑到。(不知道為啥,這次如果像以前一樣把源匯點直接連的邊設為不可走會GG……)
看一下程式碼吧。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<vector> #include<map> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define fr friend inline #define y1 poj #define mp make_pair #define pr pair<int,int> #define fi first #define sc second #define pb push_back #define I puts("Oops") using namespace std; typedef long long ll; const int M = 605; const int N = 40005; const ll INF = 1e14; const double eps = 1e-7; ll read() { ll ans = 0,op = 1;char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();} while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar(); return ans * op; } struct edge { ll next,to,from,v; }e[N<<1]; ll Ti,head[M],cur[M],deg[M],x[N],y[N],z[N],n,m,g,ecnt = -1,S,T,S1,T1,tot,p; ll dep[M],maxn,st,ed; queue <ll> q; void add(int x,int y,ll z) { e[++ecnt].to = y; e[ecnt].next = head[x]; e[ecnt].v = z; head[x] = ecnt; } bool bfs(int s,int t) { while(!q.empty()) q.pop(); rep(i,0,n+3) cur[i] = head[i]; memset(dep,-1,sizeof(dep)); dep[s] = 0,q.push(s); while(!q.empty()) { int k = q.front();q.pop(); for(int i = head[k];~i;i = e[i].next) { if(e[i].v && dep[e[i].to] == -1) dep[e[i].to] = dep[k] + 1,q.push(e[i].to); } } return dep[t] != -1; } ll dfs(int s,int t,ll lim) { if(s == t || !lim) return lim; ll flow = 0; for(int i = cur[s];~i;i = e[i].next) { cur[s] = i; if(dep[e[i].to] != dep[s] + 1) continue; ll f = dfs(e[i].to,t,min(lim,e[i].v)); if(f) { e[i].v -= f,e[i^1].v += f; flow += f,lim -= f; if(!lim) break; } } if(!flow) dep[s] = -1; return flow; } ll dinic(int s,int t) { ll maxflow = 0; while(bfs(s,t)) maxflow += dfs(s,t,INF); return maxflow; } bool checkmax(ll k) { memset(head,-1,sizeof(head)),ecnt = -1; rep(i,1,m) add(x[i],y[i],min(k,z[i])),add(y[i],x[i],0); ll now = dinic(S,T); return now == g; } ll solvemax() { ll L = 0,R = maxn,cur = 0; while(L <= R) { ll mid = (L+R) >> 1; if(checkmax(mid)) cur = mid,R = mid - 1; else L = mid + 1; } return cur * p; } bool checkmin(ll k) { memset(head,-1,sizeof(head)),memset(deg,0,sizeof(deg)); ecnt = -1,tot = 0; rep(i,1,m) { if(z[i] < k) return 0; add(x[i],y[i],z[i] - k),add(y[i],x[i],0); deg[x[i]] += k,deg[y[i]] -= k; } S1 = n + 1,T1 = S1 + 1; rep(i,1,n) { if(deg[i] < 0) add(S1,i,-deg[i]),add(i,S1,0); else add(i,T1,deg[i]),add(T1,i,0),tot += deg[i]; } add(T,S,INF),add(S,T,0); ll now = dinic(S1,T1); if(now != tot) return 0; head[S1] = head[T1] = -1; ll cur = dinic(S,T); return cur == g; } ll solvemin() { ll L = 0,R = maxn,cur = 0; while(L <= R) { ll mid = (L+R) >> 1; if(checkmin(mid)) cur = mid,L = mid + 1; else R = mid - 1; } return cur * (ll)p; } int main() { Ti = read(); while(Ti--) { n = read(),m = read(),S = read() + 1,T = read() + 1,p = read(); memset(head,-1,sizeof(head)),maxn = 0,ecnt = -1; rep(i,1,m) { x[i] = read() + 1,y[i] = read() + 1,z[i] = read(); add(x[i],y[i],z[i]),add(y[i],x[i],0),maxn = max(maxn,z[i]); } g = dinic(S,T); printf("%lld ",solvemax()); printf("%lld\n",solvemin()); } return 0; }