LOJ2477 「九省联考 2018」劈配

这道题目的题意又好绕啊。这种超级长的题面就不能附一个简要题意吗?点名批评 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;
}
posted @ 2022-04-12 19:28  Point_King  阅读(33)  评论(1编辑  收藏  举报