2019.02.26【九省联考2018】【BZOJ5251】【洛谷P4382】劈配(变形匈牙利)(二分答案)

BZOJ传送门

洛谷传送门


解析:

对于第一问,将匈牙利变形,改为每次在一定等级的志愿里面找未匹配的导师,对于改变导师的排名靠前的选手,也是在同一等级里面找匹配。

对于第二问,显然可以二分答案。

但是每次都跑一遍前 k k k的匈牙利结果太浪费时间了不是吗。

那就开一个结构体,记录一下前缀的匈牙利结果,每次二分check的时候,直接开一个tmp的图,读取前缀匈牙利结果,然后跑就是了。

跑得还挺快的,基本吊打Dinic。


代码:

#include
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num; 
	}
}
using namespace IO;

cs int N=201,M=201;

int T,C;
int n,m;
int mx[N];
vector<int> want[N][N];
int a[N][M];
int s[N];
bool vis[M];

struct Hungarian{
	int tutor[N];
	vector<int> stu[M];
	void clear(){
		memset(tutor,0,sizeof tutor);
		for(int re i=1;i<=m;++i)stu[i].clear();
	}
	
	bool find(int u,int lev){
		vector<int> &vec=want[u][lev];
		for(int re j=0;j<vec.size();++j){
			int t=vec[j];
			if(!vis[t]){
				vis[t]=true;
				if(stu[t].size()<mx[t]){
					tutor[u]=t;
					stu[t].push_back(u);
					return true;
				}
				for(int re i=0;i<mx[t];++i){
					int s=stu[t][i];
					if(find(s,a[s][t])){
						tutor[u]=t;
						stu[t][i]=u;
						return true;
					}
				}
			}
		}
		return false;
	}
}G[201],tmp;

inline void solve1(){
	for(int re i=1;i<=n;++i){
		memset(vis,0,sizeof vis);
		for(int re j=1;j<=m;++j)if(tmp.find(i,j))break;
		G[i]=tmp;
	}
}

inline bool check(int u,int lim){
	tmp=G[lim-1];
	memset(vis,0,sizeof vis);
	for(int re i=1;i<=s[u];++i)if(tmp.find(u,i))return true;
	return false;
}

inline void init(){
	for(int re i=1;i<=n;++i)
	for(int re j=1;j<=m;++j)want[i][j].clear();
	tmp.clear();
	for(int re i=1;i<=n;++i)G[i].clear();
}

int ans[N];
signed main(){
	T=getint();
	C=getint();
	while(T--){
		init();
		n=getint();
		m=getint();
		for(int re i=1;i<=m;++i)mx[i]=getint();
		for(int re i=1;i<=n;++i)
		for(int re j=1;j<=m;++j)
		if(a[i][j]=getint())want[i][a[i][j]].push_back(j);
		for(int re i=1;i<=n;++i)s[i]=getint(),a[i][0]=m+1;
		solve1();
		for(int re i=1;i<=n;++i)cout<<(ans[i]=a[i][tmp.tutor[i]])<<" ";
		cout<<"\n";
		for(int re i=1;i<=n;++i){
			if(ans[i]<=s[i]){
				cout<<"0 ";
				continue;
			}
			int l=0,r=i-1;
			while(l<r){
				int mid=(l+r+1)>>1;
				if(check(i,mid))l=mid;
				else r=mid-1;
			}
			cout<<i-l<<" ";
		}
		cout<<"\n";
	}
	return 0;
}

你可能感兴趣的:(二分答案,二分图匹配)