【校內模擬】fortress(費用流)(一般圖最大匹配)

題面見校內OJ4694


題解:

首先來看標程的不用費用流的沒什麼拓展性的做法。

考慮對於每一條狗,我們建立如下的七個點
在這裏插入圖片描述
然後對於每個能夠打到這條狗的炮臺,向這條狗的五個藍點全部連邊。

然後跑一般圖最大匹配,考慮這條狗被攻擊到若干次之後內部能夠形成的最大匹配。
在這裏插入圖片描述
顯然就是這個時候造成的傷害值就是內部能夠形成的最大匹配。

800個點跑一般圖最大匹配,隨便寫一個帶花樹或者Tutte矩陣就行了(由於圖十分稠密,帶花樹跑得很快)。

接下來讓我們忘記上面那個沒有拓展性的做法。

仔細考慮一下這個問題,顯然是一個衝突貪心,雖然貪心代價不太好算,我們還是考慮利用費用流。

每條狗建立這樣五個點:
在這裏插入圖片描述
然後源點向每個炮臺連容量爲1的邊,炮臺向能夠打到的狗的5號點連容量爲1的邊。

現在把每條狗的五個點連向T的邊按照515-1邊權降序賦值,然後跑最小費用最大流。

接着把所有與T直接相連的邊掃一遍,算貢獻。顯然這樣每條狗流滿了的邊是一個前綴。

流了幾條邊就代表扣了幾點耐久度。

現在有一個問題,我們發現流滿兩個4不如流一個4之後再流一個三。

考慮到需要退流重流,發現是一個一般圖最大匹配。考場上寫了Tutte矩陣的做法就A了。

(費用流or直接建圖)+(帶花樹orTutte矩陣求秩)四種寫法我全部寫了一遍。

由於兩種方式建出來的圖都十分稠密,所以帶花樹求最大匹配收斂賊快,比Tutte矩陣快得多。


代碼(費用流+帶花樹):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=1e4+7;

int n,m,S,T,tot;

struct edge{int to,cap,w,rev;};
std::vector<edge> G[N];
typedef std::vector<edge>::iterator iter;
iter cur[N];
inline void adde(int u,int v,int cap,int cost){
	G[u].push_back((edge){v,cap,cost,(int)G[v].size()});
	G[v].push_back((edge){u,0,-cost,(int)G[u].size()-1});
}

inline int find_T(int u){
	for(iter e=G[u].begin();e!=G[u].end();++e)if(e->to==T)return e->cap;
	return 0;
}

int tot_cost,tot_flow;

int dis[N],vis[N];
inline bool SPFA(){
	std::queue<int> q;
	memset(dis+1,0x3f,sizeof(int)*tot);dis[S]=0;q.push(S);
	for(int re i=1;i<=tot;++i)cur[i]=G[i].begin();
	while(!q.empty()){
		int u=q.front();vis[u]=false;q.pop();
		for(iter e=G[u].begin();e!=G[u].end();++e)
		if(e->cap&&dis[e->to]>dis[u]+e->w){
			dis[e->to]=dis[u]+e->w;
			if(!vis[e->to])vis[e->to]=true,q.push(e->to);
		}
	}
	return dis[T]<1e9;
}

int st[N],tp;
int dfs(int u,cs int flow){
	if(u==T){
		tot_cost+=flow*dis[T];
		tot_flow+=flow;
		return flow;
	}
	vis[u]=true;int ans=0;
	for(iter &e=cur[u];e!=G[u].end();++e){
		if(e->cap&&dis[e->to]==dis[u]+e->w&&!vis[e->to]){
			int delta=dfs(e->to,std::min(flow-ans,e->cap));
			if(delta){
				e->cap-=delta;
				G[e->to][e->rev].cap+=delta;
				if((ans+=delta)==flow)break;
			}
		}
	}
	vis[u]=false;
	return ans;
}

inline void Flow(){//最小費用最大流 
	tot_cost=tot_flow=0;
	while(SPFA())dfs(S,0x3f3f3f3f);
}

std::vector<int> pre[N],bin[N];
namespace G_Match{
	cs int N=1e2+1;
	int m;
	std::vector<int> G[N];
	int con[N][N];
	inline void adde(int u,int v){if(con[u][v])return ;
		con[u][v]=con[v][u]=true;
		G[u].push_back(v),G[v].push_back(u);
	}
	int match[N],q[N],hd,tl;
	int fa[N],pre[N],tim,tic[N],typ[N];
	inline int gf(int u){return u==fa[u]?u:(fa[u]=gf(fa[u]));}
	inline int LCA(int u,int v){
		for(++tim;;std::swap(u,v))if(u){
			u=gf(u);
			if(tic[u]==tim)return u;
			else tic[u]=tim,u=pre[match[u]];
		}
	}
	inline void shrink(int u,int v,int p){
		while(gf(u)!=p){
			pre[u]=v,v=match[u];
			if(typ[v]==2)typ[v]=1,q[++tl]=v;
			if(gf(u)==u)fa[u]=p;
			if(gf(v)==v)fa[v]=p;
			u=pre[v];
		}
	}
	inline int aug(int s){
		for(int re i=1;i<=m;++i)fa[i]=i;
		memset(typ,0,sizeof(int)*(m+1));
		memset(pre,0,sizeof(int)*(m+1));
		typ[q[hd=tl=1]=s]=1;
		while(hd<=tl){
			int u=q[hd++];
			for(int re v:G[u]){
				if(gf(v)==gf(u)||typ[v]==2)continue;
				else if(!typ[v]){
					typ[v]=2,pre[v]=u;
					if(!match[v]){
						for(int re t=v,las,tp;t;t=las){
							las=match[tp=pre[t]];
							match[t]=tp,match[tp]=t;
						}
						return true;
					}
					typ[match[v]]=1,q[++tl]=match[v];
				}
				else if(typ[v]==1){
					int p=LCA(u,v);
					shrink(u,v,p);
					shrink(v,u,p); 
				}
			}
		}
		return false;
	}
	
	inline int calc(){
		int ans=0;
		for(int re i=1;i<=m;++i)ans+=(!match[i]&&aug(i));
		return ans;
	}
	
	inline void clear(){
		for(int re i=1;i<=m;++i)G[i].clear(),memset(con[i],0,sizeof(int)*(m+1));
		memset(match,0,sizeof(int)*(m+1));
	}
	
	inline int solve(){
		int siz=0;
		for(int re i=1;i<=n;++i)siz=std::max(siz,(int)bin[i].size());
		if(siz<=1)return 0;
		clear();
		for(int re i=1;i<=n;++i){
			for(int re j=0;j<bin[i].size();++j)
			for(int re k=j+1;k<bin[i].size();++k)adde(bin[i][j],bin[i][k]);
		}
		return calc();
	}
}

int idc[N],idd[N][6];
inline void solve(){
	scanf("%d%d",&n,&m);tot=0;
	for(int re i=1;i<=n;++i)idc[i]=++tot,bin[i].clear();
	for(int re i=1;i<=m;++i)
	for(int re j=1;j<=5;++j)idd[i][j]=++tot;
	S=++tot,T=++tot;
	for(int re i=1;i<=tot;++i)G[i].clear();
	for(int re i=1;i<=n;++i)adde(S,idc[i],1,0);
	for(int re i=1;i<=m;++i){
		pre[i].clear();
		adde(idd[i][5],idd[i][4],4,0);
		adde(idd[i][4],idd[i][3],3,0);
		adde(idd[i][3],idd[i][2],2,0);
		adde(idd[i][2],idd[i][1],1,0);
		adde(idd[i][5],T,1,-105);
		adde(idd[i][4],T,1,-99);
		adde(idd[i][3],T,1,-99);
		adde(idd[i][2],T,1,-89);
		adde(idd[i][1],T,1,-84);
	}
	for(int i=1;i<=n;++i){
		int k,t;scanf("%d",&k);std::vector<int> v;
		while(k--){scanf("%d",&t);v.push_back(t);pre[t].push_back(i);}
		std::sort(v.begin(),v.end());
		for(int re j=0;j<(int)v.size();++j)adde(idc[i],idd[v[j]][5],1,0);
	}
	Flow();assert(tot_flow==n);
	int res=m*3;
	for(iter e=G[T].begin();e!=G[T].end();++e){
		if(e->cap&&(e->w==84||e->w==89||e->w==99)){
			--res;
		}
	}int &cnt=G_Match::m;cnt=0;
	for(int i=1;i<=m;++i){
		int t1=find_T(idd[i][4]),t2=find_T(idd[i][3]);
		if(!t1&&!t2)++res;
		if(t1^t2){++cnt;
			for(int re j=0;j<(int)pre[i].size();++j)bin[pre[i][j]].push_back(cnt);
		}
	}
	res+=G_Match::solve();
	std::cout<<res<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("fortress.in","r",stdin);
#else 
#ifndef ONLINE_JUDGE
	freopen("fortress.in","r",stdin);freopen("fortress.out","w",stdout);
#endif
#endif
	int T;scanf("%d",&T);srand(time(0));
	while(T--)solve();
	return 0;
}

代碼(直接建圖跑一般圖最大匹配,我寫的Tutte矩陣求秩,慢得嚇人):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

namespace G_Match{
	cs int N=8e2+7;
	int a[N][N],b[N][N],m;
	
	cs int mod=1e9+7;
	inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
	inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
	inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
	inline int power(int a,int b,int res=1){
		for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
		return res;
	}
	inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
	inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
	inline void Mul(int &a,int b){a=mul(a,b);}
	
	inline int calc(){//rank
		for(int re i=1;i<=m;++i)
		for(int re j=1;j<=m;++j){
			if(i<j){
				if(a[i][j])b[i][j]=mul(abs(rand()),abs(rand()));
				else b[i][j]=0;
			}
			else if(i==j)b[i][j]=0;
			else b[i][j]=dec(0,b[j][i]);
		}
		int ans=0;
		for(int re i=1;i<=m;++i){
			int p;for(p=i;p<=m;++p)if(b[p][i])break;
			if(p>m)continue;++ans;
			if(i!=p)for(int re j=i;j<=m;++j)std::swap(b[i][j],b[p][j]);
			int inv=power(b[i][i],mod-2);
			for(int re j=i;j<=m;++j)Mul(b[i][j],inv);
			for(int re j=i+1,t;j<=m;++j)if(!!(t=b[j][i]))
			for(int re k=i;k<=m;++k)Dec(b[j][k],mul(t,b[i][k]));
		}
		return ans>>1;
	}
	inline void adde(int u,int v){a[u][v]=a[v][u]=true;}
	inline void clear(){
		memset(a,0,sizeof a);
	}
	inline int solve(){return std::min(calc(),calc());}
}
using G_Match::adde; 

cs int N=1e2+7;
int &tot=G_Match::m;
int n,m;

int idc[N],idd[N][10];
inline void solve(){
	scanf("%d%d",&n,&m),tot=0;
	for(int re i=1;i<=n;++i)idc[i]=++tot;
	for(int re i=1;i<=m;++i)
	for(int re j=0;j<7;++j)idd[i][j]=++tot;
	G_Match::clear();
	for(int re i=1;i<=m;++i){
		adde(idd[i][0],idd[i][1]);
		adde(idd[i][1],idd[i][2]);
		adde(idd[i][2],idd[i][3]);
		adde(idd[i][3],idd[i][4]);
		adde(idd[i][4],idd[i][0]);
		
		adde(idd[i][5],idd[i][0]);
		adde(idd[i][5],idd[i][1]);
		adde(idd[i][5],idd[i][2]);
		adde(idd[i][5],idd[i][3]);
		adde(idd[i][5],idd[i][4]);
		
		adde(idd[i][6],idd[i][0]);
		adde(idd[i][6],idd[i][1]);
		adde(idd[i][6],idd[i][2]);
		adde(idd[i][6],idd[i][3]);
		adde(idd[i][6],idd[i][4]);
	}
	for(int re i=1;i<=n;++i){
		int k,v;scanf("%d",&k);
		while(k--){
			scanf("%d",&v);
			adde(idc[i],idd[v][0]);
			adde(idc[i],idd[v][1]);
			adde(idc[i],idd[v][2]);
			adde(idc[i],idd[v][3]);
			adde(idc[i],idd[v][4]);
		}
	}
	std::cout<<G_Match::calc()-n<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("fortress.in","r",stdin);
#endif
	int T;scanf("%d",&T);srand(time(0));
	while(T--)solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章