2020牛客暑期多校训练营(第一场)H.Minimum-cost Flow
题解思路:其实思路很简单,跑一遍费用流后记录每一条增广路的流量和花费,队这些增广路按照花费排序,之后对每次询问只需要去枚举这些增广路即可,把每条(1,x/y)的边看作是(y,x)的边,最后再对答案除y,这样就有效避免了分数出现的尴尬情况。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> PII; #define ls l,mid,rt<<1 #define rs mid+1,r,rt<<1|1 #define endl '\n' #define p4 puts("444") const int N = 50+10,M = 1e2+10; const double EPS = 1e-12; const ll mod = 1e9+7; int n,m,s,t,q,cnt,maxflow,tot; int head[N],cur[N],inq[N],pre[N],path[N]; ll dis[N],cost; struct edge{ int to,next,cap; ll cost; }E[M<<1]; struct node{ void Add(int u,int v,int w,ll c){ E[cnt].to=v; E[cnt].cap=w; E[cnt].next=head[u]; E[cnt].cost=c; head[u]=cnt++; } bool Spfa(){ for(int i=1;i<=n;i++)cur[i]=head[i],dis[i]=1e18,inq[i]=0,pre[i]=0; queue<int>que; que.push(s); inq[s]=1; dis[s]=0; while(!que.empty()){ int u=que.front();que.pop(); inq[u]=0; for(int i=head[u];~i;i=E[i].next){ int v=E[i].to; if(E[i].cap&&dis[v]>dis[u]+E[i].cost){ pre[v]=i; dis[v]=dis[u]+E[i].cost; if(!inq[v]){ inq[v]=1; que.push(v); } } } } return dis[t]==1e18 ? 0 : 1; } void Solve(){ while(Spfa()){ int flow=1e9; for(int i=t;i!=s;i=E[pre[i]^1].to)flow=min(flow,E[pre[i]].cap); maxflow+=flow; path[++tot]=0; for(int i=t;i!=s;i=E[pre[i]^1].to){ E[pre[i]].cap-=flow; E[pre[i]^1].cap+=flow; cost+=flow*E[pre[i]].cost; path[tot]+=E[pre[i]].cost; } } } void Init(){ maxflow=cost=cnt=tot=0; memset(head,-1,sizeof(head)); } }EK; int main() { while(~scanf("%d %d",&n,&m)){ EK.Init(); int a,b; ll c; for(int i=1;i<=m;i++){ scanf("%d %d %lld",&a,&b,&c); EK.Add(a,b,1,c); EK.Add(b,a,0,-c); } ll x,y; s=1,t=n; EK.Solve(); scanf("%d",&q); while(q--){ int flag=0; ll nowflow=0,nowcost=0; scanf("%lld %lld",&x,&y); for(int i=1;i<=tot;i++){ if(x*(tot-i+1)<y-nowflow){ flag=1; break; } int need=min(x,y-nowflow); nowflow+=need; nowcost+=path[i]*need; } if(flag)puts("NaN"); else cout<<nowcost/__gcd(nowcost,y)<<"/"<<y/__gcd(nowcost,y)<<endl; } } }
希望用自己的努力为自己赢得荣誉。