1. 程式人生 > >ZOJ3496 Assignment

ZOJ3496 Assignment

傳送門

這題也是真噁心……

題目大意是倆公司要運貨,每條路有容量上限。然後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;
}