【BZOJ3876】支线剧情(AHOI&JSOI2014)-有上下界费用流
测试地址:支线剧情
做法:本题需要用到有上下界费用流。
我们发现题目要求的就是,以点为源点,所有其他点为汇点的最小费用可行流,每条边的流量必须在区间内,这就是有上下界的可行流了。
我们先讨论无源汇的可行流(循环流)要怎么做,由于每条边的下界的存在,那么这条边的两个端点就有必要的出流量和必要的入流量,我们可以建一个超级源点和一个超级汇点,对于原图中每条流量区间为的边,连接和,容量为,再连,容量为。为什么要这样建呢?因为如果对这样建出来的网络跑最大流,要么从源点出发的边全部满流,这时指向汇点的边也全部满流,表示求出了一个可行流,因为新网络的流量平衡等同于旧网络的流量平衡,新网络里边的流量为,表示在旧网络中这条边的流量为。那么因为新网络的最大流等同于旧网络的可行流,所以对新网络求最小费用最大流也就等同于对旧网络求最小费用可行流了,只要把边的费用同时赋在边上即可。
可是这道题是有源汇的,这要怎么办呢?其实很简单,只要从所有汇点向源点连一条容量为正无穷的边即可,这样就可以转化为无源汇的问题,那么应用上面的方法做即可。
这道题卡时间啊……要卡进总时间的话,对于一个点,我们把上面我们建的所有边合并成一条边,因为费用为所以对答案没有影响,这样就可以卡进总时间了,但分点测试无论如何也过不去,有待学习更加好的优化方法。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1000000000ll*1000000000ll;
int n,S,T,first[310]={0},tot=1;
int laste[310],last[310];
ll dis[310];
bool vis[310];
queue<int> Q;
struct edge
{
int v,next;
ll f,c;
}e[100010];
void insert(int a,int b,ll f,ll c)
{
e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,e[tot].c=c,first[a]=tot;
e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,e[tot].c=-c,first[b]=tot;
}
void init()
{
scanf("%d",&n);
S=n+1,T=n+2;
for(int i=1;i<=n;i++)
{
int k,x;
ll t,f=0;
scanf("%d",&k);
while(k--)
{
scanf("%d%lld",&x,&t);
insert(S,x,1,t);
insert(i,x,inf,t);
f++;
}
insert(i,T,f,0);
if (i>1) insert(i,1,inf,0);
}
}
bool spfa()
{
for(int i=1;i<=T;i++)
dis[i]=inf;
dis[S]=0;
vis[S]=1;
Q.push(S);
while(!Q.empty())
{
int v=Q.front();Q.pop();
for(int i=first[v];i;i=e[i].next)
if (e[i].f&&dis[e[i].v]>dis[v]+e[i].c)
{
dis[e[i].v]=dis[v]+e[i].c;
laste[e[i].v]=i;
last[e[i].v]=v;
if (!vis[e[i].v]) Q.push(e[i].v),vis[e[i].v]=1;
}
vis[v]=0;
}
return dis[T]!=inf;
}
void mincost()
{
ll minc=0;
while(spfa())
{
int x=T;
ll maxf=inf;
while(x!=S)
{
maxf=min(maxf,e[laste[x]].f);
x=last[x];
}
x=T;
while(x!=S)
{
e[laste[x]].f-=maxf;
e[laste[x]^1].f+=maxf;
x=last[x];
}
minc+=maxf*dis[T];
}
printf("%lld",minc);
}
int main()
{
init();
mincost();
return 0;
}