【BZOJ】【P2893】【征服王】【题解】【缩点费用流】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2893

吐槽:为什么题目是征服王题面是wa2……而且我是冬马党……

而且上次提交是2013年5月……

这题……一眼缩点,缩成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

跑最大费用流

有cost的边代表这个点经过一次

那么最大费用流每次会尽量走费用多的边,即走尽量多的点,正确性由网络流保证

增广次数即为答案

复杂度?

每次增广至少有1个cost,总共有n个cost,最坏增广n次

Code:

#include<bits/stdc++.h>
#undef INT_MAX
#define INT_MAX 100000
using namespace std;
const int maxn=1010;
int getint(){
	int res=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))res=res*10+c-'0',c=getchar();
	return res;
}
int n,m,a,b,ans=0;
vector<int>G[maxn];
namespace Flow{ 
	const int maxn=2010;
	int s=0,t=2009;  
	struct edge{  
	    int u,v,cap,flow,cost;  
	    edge(int _u=0,int _v=0,int _cap=0,int _flow=0,int _cost=0):  
	        u(_u),v(_v),cap(_cap),flow(_flow),cost(_cost){}  
	};  
	vector<edge>edges;  
	vector<int>G[maxn];  
	int pre[maxn],a[maxn],d[maxn],vis[maxn],cost,flow;  
	void init(){
		edges.clear();flow=cost=0;
		for(int i=0;i<maxn;i++)G[i].clear();
	}
	void add(int u,int v,int cap,int cost){  
	    edges.push_back(edge(u,v,cap,0,cost));  
	    G[u].push_back(edges.size()-1);  
	    edges.push_back(edge(v,u,0,0,-cost));  
	    G[v].push_back(edges.size()-1);  
	}  
	bool spfa(){  
	    queue<int>q;  
	    q.push(s);  vis[s]=1;
	    memset(d,-1,sizeof d);int B=d[0];d[s]=0;a[s]=INT_MAX;  
	    while(!q.empty()){  
	        int u=q.front();q.pop();vis[u]=0;  
	        for(int i=0;i<G[u].size();i++){  
	            edge e=edges[G[u][i]];  
	            if(e.cap>e.flow&&d[e.v]<d[u]+e.cost){  
	                d[e.v]=d[u]+e.cost;  
	                pre[e.v]=G[u][i];  
	                a[e.v]=min(a[u],e.cap-e.flow);  
	                if(!vis[e.v]){  
	                    vis[e.v]=1;  
	                    q.push(e.v);  
	                }  
	            }  
	        }  
	    }  
	    if(d[t]==B)return false;
	    cost+=d[t]*a[t]; 
	    flow+=a[t];  
	    for(int u=t;u!=s;u=edges[pre[u]].u){  
	        edges[pre[u]].flow+=a[t];  
	        edges[pre[u]^1].flow-=a[t];  
	    }return d[t]>0;  
	}  
	void deb(){
		for(int i=0;i<edges.size();i++)if(i%2==0)
		printf("%d->%d cap:%d cost:%d\n",edges[i].u,edges[i].v,edges[i].cap,edges[i].cost);
	}
}
int bel[maxn];
namespace SCC{
	int cnt,dfn[maxn],low[maxn],tot;
	stack<int>S;short ins[maxn];
	void init(){
		cnt=tot=0;
		memset(dfn,0,sizeof dfn);
		memset(low,0,sizeof low);
		memset(ins,0,sizeof ins);
		memset(bel,0,sizeof bel);
	}
	void dfs(int u){
		ins[u]=1;S.push(u);
		dfn[u]=low[u]=++tot;
		for(int i=0,v;i<G[u].size();i++){
			if(!dfn[v=G[u][i]]){
				dfs(v);
				low[u]=min(low[u],low[v]);
			}else if(ins[v]) low[u]=min(low[u],dfn[v]);	
		}
		if(low[u]==dfn[u]){
			cnt++;int v;
			do{  
          	  v=S.top();S.pop();  
          	  bel[v]=cnt;  
          	  ins[v]=0;  
        	}while(u!=v);   
		}
	}
	void tarjan(){
		for(int i=1;i<=n;i++)if(!dfn[i])
		dfs(i);
	}
}
int st[maxn],ed[maxn],mp[maxn][maxn];
set<pair<int,int> >St;
void init(){
	memset(mp,0,sizeof mp);
	scanf("%d%d%d%d",&n,&m,&st[0],&ed[0]);
	for(int i=1;i<=n;i++)G[i].clear();
	for(int i=1;i<=st[0];i++)scanf("%d",&st[i]);
	for(int i=1;i<=ed[0];i++)scanf("%d",&ed[i]);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		G[u].push_back(v);
	}SCC::init();Flow::init();
}
void solve(){
	SCC::tarjan();
	for(int i=1;i<=SCC::cnt;i++)Flow::add(i<<1,i<<1|1,1,1),Flow::add(i<<1,i<<1|1,INT_MAX,0);
	for(int i=1;i<=st[0];i++)Flow::add(Flow::s,bel[st[i]]<<1,INT_MAX,0);
	for(int i=1;i<=ed[0];i++)Flow::add(bel[ed[i]]<<1|1,Flow::t,INT_MAX,0);	
	for(int i=1;i<=n;i++)for(int j=0;j<G[i].size();j++){
		int u=bel[i],v=bel[G[i][j]];
		if(mp[u][v]||u==v)continue;
		Flow::add(u<<1|1,v<<1,INT_MAX,0);
		mp[u][v]=mp[v][u]=1;
	}ans=0;
	//Flow::deb();
	while(Flow::spfa())ans++;
	if(Flow::cost!=SCC::cnt)puts("no solution");
	else printf("%d\n",ans);
}
int main(){
	int _=getint();
	while(_--){
		init();
		solve();
	}
	return 0;
}




你可能感兴趣的:(bzoj)