BZOJ2893: 征服王
费用流 的 奇妙世界
题解:
哇塞好神奇!
♪(´∇`*)
竟然还有如此厚颜无耻**巧妙**的费用流!
好啦以下是抄的题解:
首先缩点,变成DAG
“见到有向图60%是缩点”——某大佬
然后就是一个DAG上的一条边可以重复走最小路径覆盖。
DAG的最小路径覆盖可以用二分图匹配来解决。
可重复的就要用费用流了:
对于一个点u,拆成u->u’ ,连两条边,一条cap=1 cost=1,另一条cap=inf cost=0
s->u cap=inf cost=0,u’ -> t cap=inf cost=0
对于原图的边(u,v): u’->v cap=inf cost=0
跑最大费用流,每次增广1的流量,增广次数即为答案。
有cost的边代表这个点经过一次,那么最大费用流每次会尽量走费用多的边,即走尽量多的点,正确性由网络流保证。
复杂度?
每次增广至少有1个cost,总共有n个cost,最坏增广n次。
对了,前面一个namespace里怎么使用后面namespace的变量啊。。。语言渣。。。
你会看到为了在MXF中用scc我把它放在全局了。。。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 2017;
const int M = 20010;
const int INF = 0x3f3f3f3f;
int n,m,A[N],B[N],scc;
namespace MXF{
struct Edge{
int from,to,nxt,cap,flow,cost;
} e[200005];
int head[N], ec=1;
void add(int a,int b,int cap,int cost){
ec++; e[ec].cap=cap; e[ec].flow=0; e[ec].cost=cost;
e[ec].from=a; e[ec].to=b; e[ec].nxt=head[a]; head[a]=ec;
}
void add2(int a,int b,int cap,int cost){
// D(a); D(b); D(cap); D(cost); E;
add(a,b,cap,cost); add(b,a,0,-cost);
}
int S,T,SS;
void init(){
ec=1; memset(head,0,sizeof(head));
S=N-7; T=S+1; SS=S+2;
}
int dis[N],pre[N]; bool vis[N];
bool spfa(){
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
queue<int> q; q.push(S); vis[S]=true; dis[S]=0; pre[S]=0;
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=false;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].cap>e[i].flow && dis[v]>dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost; pre[v]=i;
if(!vis[v]){ vis[v]=true; q.push(v); }
}
}
}
return dis[T]!=INF;
}
void solve(){
int cost=0, last, a, cnt=0;
add2(S,SS,1,0);
while(spfa()){
a=INF; cnt++; last=cost;
for(int i=pre[T];i;i=pre[e[i].from]){ a=min(a,e[i].cap-e[i].flow); }
for(int i=pre[T];i;i=pre[e[i].from]){ e[i].flow+=a; e[i^1].flow-=a; cost+=a*e[i].cost; }
// D(a); D(cost); E;
if(cost==last) break;
add2(S,SS,1,0);
}
if(-cost==scc) printf("%d\n",cnt-1);
else puts("no solution");
}
}
namespace TARJAN{
struct Edge{ int to,next; } e[M];
int head[N], ec=0;
void add(int a,int b){
ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
}
int low[N],dfn[N],belong[N],tim; bool inq[N];
stack<int> sta;
void init(){
ec=0; memset(head,0,sizeof(head));
scc=0; tim=0; while(!sta.empty()) sta.pop();
memset(dfn,0,sizeof(dfn)); memset(inq,false,sizeof(inq));
}
void tarjan(int u){
// D(u); E;
low[u]=dfn[u]=++tim;
sta.push(u); inq[u]=true;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(inq[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
int x=0; scc++;
while(x!=u){
x=sta.top(); sta.pop(); inq[x]=false;
belong[x]=scc;
}
}
}
void tarjan(){ for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } }
void work(){
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(belong[u]!=belong[v])
MXF::add2(belong[u]+scc,belong[v],INF,0);
}
}
for(int i=1;i<=scc;i++){
MXF::add2(i,i+scc,1,-1);
MXF::add2(i,i+scc,INF,0);
}
for(int i=1;i<=A[0];i++) MXF::add2(MXF::SS,belong[A[i]],INF,0);
for(int i=1;i<=B[0];i++) MXF::add2(belong[B[i]]+scc,MXF::T,INF,0);
}
}
void input(){
cin>>n>>m>>A[0]>>B[0];
MXF::init();
TARJAN::init();
for(int i=1;i<=A[0];i++) scanf("%d",A+i);
for(int i=1;i<=B[0];i++) scanf("%d",B+i);
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
TARJAN::add(u,v);
}
}
int main(){
freopen("a.in","r",stdin);
int cas; cin>>cas;
while(cas--){
input();
TARJAN::tarjan();
// D(scc); E;
// for(int i=1;i<=n;i++)
// cout<<TARJAN::belong[i]<<endl;
TARJAN::work();
MXF::solve();
}
}