1. 程式人生 > 其它 >LOJ3051 「十二省聯考 2019」皮配

LOJ3051 「十二省聯考 2019」皮配

這道題目的題意又好繞啊。這種超級長的題面就不能附一個簡要題意嗎?點名批評 NOIP2021T4 。

一邊試錯搜尋,加入每一個人的每一檔,一邊用網路流 check 即可解決第一問,(感覺最近網路流演算法老是被忘記。。。)

對於第二問,我們考慮也用網路流來試錯。具體的,我們處理出前 i 個人選擇好的網路流的圖。由於每次增廣的時候只會拓展 1 ,所以我們可以暴力處理出哪些點可以到達匯點,每次加入一條邊的時候直接 check 起點能否到達源點(此題中一定能),終點能否到達匯點。

你會發現此時和第一問是可以一起做的。


這裡就用到了每次流量遞增一,所以可以用暴力維護出哪些點是可行的,還算是一個比較有用的技巧。在優化二分圖匹配的時候儘量不要往網路流的方向去思考,用暴力增廣單次的方法往往是比較有效的。

可以再舉 pj 的 round1 的 T2 ,也是利用這個性質進行優化的。

#include<bits/stdc++.h>
using namespace std;

const int N=2e2+5,M=2e2+5;
const int INF=1e9+7;

int n,m,b[M],s[N];
vector<int> bag[N][M];

int id[N],ID[M];

struct Dinic{
	int tot,from,to;
	struct Edge{int nxt,to,flow;}e[N*M*2];int fir[N+M],e_siz;
	void add(int u,int v,int w){e[++e_siz]=(Edge){fir[u],v,w},fir[u]=e_siz;}
	void init(){
		for(int i=1;i<=tot;++i) fir[i]=0;
		tot=0,from=++tot,to=++tot,e_siz=1;
	}

	int cur[N+M],dis[N+M],vis[N+M],q[N+M],h,t;

	bool bfs(){
		for(int i=1;i<=tot;++i) cur[i]=fir[i],dis[i]=INF;
		dis[from]=0,h=1,t=0,q[++t]=from;
		while(h<=t){
			int u=q[h++];
			for(int i=fir[u];i;i=e[i].nxt){
				int v=e[i].to;
				if(!e[i].flow||dis[v]<=dis[u]+1) continue;
				dis[v]=dis[u]+1,q[++t]=v;
			}
		}
		return dis[to]!=INF;
	}
	int dfs(int u,int flow){
		if(u==to) return flow;
		int res=0;vis[u]=true;
		for(int i=cur[u];i&&flow;i=e[i].nxt){
			int v=e[i].to;cur[u]=i;
			if(vis[v]||!e[i].flow||dis[v]!=dis[u]+1) continue;
			int tmp=dfs(v,min(flow,e[i].flow));
			e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
		}
		return vis[u]=false,res;
	}
	int Max_Flow(){
		int res=0;
		while(bfs()) res+=dfs(from,INF);
		return res;
	}

	int tag[N+M];

	void get(){
		for(int i=1;i<=tot;++i) tag[i]=false;
		tag[to]=true,h=1,t=0,q[++t]=to;
		while(h<=t){
			int u=q[h++];
			for(int i=fir[u];i;i=e[i].nxt){
				int v=e[i].to;
				if(!e[i^1].flow||tag[v]) continue;
				tag[v]=true,q[++t]=v;
			}
		}
	}
}mf;

int tag[N][M],res1[N],res2[N];

int solve(){
	cin>>n>>m;

	for(int i=1;i<=m;++i) scanf("%d",&b[i]);
	for(int i=1;i<=n;++i) for(int j=0;j<=m+1;++j) bag[i][j].clear();
	for(int i=1;i<=n;++i) for(int j=1,x;j<=m;++j) scanf("%d",&x),bag[i][x].push_back(j);
	for(int i=1;i<=n;++i) scanf("%d",&s[i]);

	mf.init();
	for(int i=1;i<=n;++i) id[i]=++mf.tot;
	for(int i=1;i<=m;++i) ID[i]=++mf.tot;

	for(int i=1;i<=n;++i) mf.add(mf.from,id[i],1),mf.add(id[i],mf.from,0);
	for(int i=1;i<=m;++i) mf.add(ID[i],mf.to,b[i]),mf.add(mf.to,ID[i],0);
	
	for(int i=1;i<=n;++i) res1[i]=m+1,res2[i]=i;

	for(int i=1;i<=n;++i){
		mf.get();
		for(int j=1;j<=m;++j) tag[i][j]=mf.tag[ID[j]];
		for(int j=1;j<=m;++j){
			bool flag=false;
			for(int x:bag[i][j]) flag|=tag[i][x];
			if(flag){res1[i]=j;break;}
		}
		for(int x:bag[i][res1[i]]) mf.add(id[i],ID[x],1),mf.add(ID[x],id[i],0);
		mf.Max_Flow();
	}

	for(int i=1;i<=n;++i){
		int L=1,R=i;
		while(L<=R){
			int Mid=(L+R)>>1;bool flag=false;
			for(int j=1;j<=s[i];++j) for(int x:bag[i][j]) flag|=tag[Mid][x];
			if(flag) L=Mid+1,res2[i]=i-Mid;else R=Mid-1;
		}
	}

	for(int i=1;i<=n;++i) printf("%d ",res1[i]);
	printf("\n");
	for(int i=1;i<=n;++i) printf("%d ",res2[i]);
	printf("\n");

	return 0;
}

int main(){
	// freopen("1.in","r",stdin);
	// freopen("1.out","w",stdout);

	int T,c;cin>>T>>c;while(T--) solve();
	return 0;
}