题意: 给一个n个点m条边的有向带权图,q次询问,每次规定每条边的容量为u/v,你需要输出总流量为1时,从点1到点n的最小费用(分数表示),若到达的流量不足为1,则输出NaN;
题解:标准费用流模板,但是q的范围是1e5,所以我们要先跑一次费用流,把有用的信息记录下来,即把每一条增广路径的费用记录下来。
考虑放缩,同时乘以v,则总流量为v,每条边的容量为u,这是算出来的总费用除以v即为答案。我们可以在询问之前,预处理得到所有增广路的费用,每次进行一次SPFA算法后,就能得到一条增广路的费用,将其记录于path数组中(按增广顺序能保证每条增广路费用是升序的),并求其前缀和,方便后续询问的处理。
查询之前,先预处理得到:
(1)path[a],下标从0开始,表示单位容量(流量跑满,流量=容量)时第a+1条增广路费用。
(2)sum[a],下标从1开始,表示单位容量(流量跑满,流量=容量)时前a条增广路总费用。
接下来处理每次询问。
假设v=a*u+b(b<u),即前a条增广路流量都为u,第a+1条增广路流量达不到u,流量为b。
由于sum和path是之前预处理得到的单位容量情况下的费用,所以sum[a]要乘以u得到流量为u时前a条增广路总费用,path[a]要乘以b得到流量为b时第a+1条增广路费用,则ans=(sum[a]*u+path[a]*b)/v。
卡int。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=INT_MAX;
const int maxn=1000+5,maxm=10000+5;
int n,m,s,t,q,cnt=0;
int head[maxn];
int dis[maxn];
ll maxflow=0;
ll minfee=0;
bool vis[maxn];
vector<ll> path;
ll sum[maxn];
struct node
{
int e;
int nxt;
int w;//指权值
int f;//指流量
}edge[maxm<<1];
inline void add(int u,int v,int flow,int cost)
{
edge[cnt].e=v;
edge[cnt].w=cost;
edge[cnt].f=flow;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
inline int read()
{
int ans=0;
char r=getchar();
while(r<'0'||r>'9')r=getchar();
while(r>='0'&&r<='9')
{
ans=ans*10+r-'0';
r=getchar();
}
return ans;
}
bool spfa(int u,int v)
{
for(int i=0;i<maxn;i++)dis[i]=INF;//spfa基本操作
memset(vis,false,sizeof(vis));//spfa的队列排重优化
dis[u]=0;
deque<int>d;//spfa的slf优化
d.push_back(u);
vis[u]=true;
while(!d.empty())
{
int ui=d.front();d.pop_front();vis[ui]=false;//ui是当前松弛的边的起点
for(int i=head[ui];i!=-1;i=edge[i].nxt)
{
int vi=edge[i].e;int w=edge[i].w;int f=edge[i].f;//vi是当前松弛的边的终点
if(f&&dis[vi]>dis[ui]+w)//如果当前边有残量且是最短路上的边就对其进行松弛
{
dis[vi]=dis[ui]+w;
if(!vis[vi])
{
if(!d.empty()&&dis[vi]<dis[d.front()])d.push_front(vi);
else d.push_back(vi);
}
}
}
}
return dis[t]!=INF;
}
int dfs(int u,int flow)
{
if(u==t)return flow;
int detla=flow;
vis[u]=true;
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].e;int w=edge[i].w;int f=edge[i].f;
if(f&&!vis[v]&&dis[v]==dis[u]+w)//注意,一个点只能访问一次,这个可以用vis来维护(vis在spfa模块和dfs模块中都被用到,注意初始化)
{
int d=dfs(v,min(f,detla));
detla-=d;
edge[i].f-=d;edge[i^1].f+=d;
minfee+=d*w;//更新费用(单价x流量)
if(detla==0)break;
}
}
vis[u]=false;
return flow-detla;
}
ll dinic()
{
maxflow=0;
path.clear();
while(spfa(s,t)){
path.push_back(dis[t]); //记录增广路径的费用
memset(vis,false,sizeof(vis));
maxflow+=dfs(s,INF);
}
return maxflow;
printf("%d %d",maxflow,minfee);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
memset(head,-1,sizeof(head));
sum[0]=0;
cnt=0;
s=1,t=n;
int ui,vi,wi=1,fi;
for(int i=0;i<m;i++){
ui=read();vi=read();fi=read();//fi是单位流量费用,wi是最大流量
add(ui,vi,wi,fi);//正向边的权值为fi,流量为wi
add(vi,ui,0,-fi);//反向边的权值为-fi,流量为0
}
dinic();
ll pnum=path.size(),u,v;
// for(int i=0;i<pnum;i++){
// cout<<path[i]<<" ";
// }
// cout<<endl;
for(ll i=0;i<pnum;i++){
sum[i+1]=sum[i]+path[i];
}
scanf("%d",&q);
while(q--){
scanf("%lld%lld",&u,&v);
if(u*pnum<v) printf("NaN\n");
else{
ll a=v/u;
ll b=v%u;
ll ans=sum[a]*u+path[a]*b;
ll k=__gcd(ans,v);
ans/=k;
v/=k;
printf("%lld/%lld\n",ans,v);
}
}
}
return 0;
}
View Code