luogu P4382 [九省联考2018]劈配

luogu

我记得我第一次做这道题的时候屁都不会qwq

先考虑第一问,暴力是依次枚举每个人,然后从高到低枚举志愿,枚举导师,能选就选.但是可以发现前面的人选的导师可能会导致后面的人本来可以选到这个志愿,但是不能选.这个问题是不是有点眼熟?你可以理解成二分图匹配问题,就是对于每个人,枚举志愿,然后把这个志愿对应的一些导师连边,如果跑匹配能跑出来那么他就是这个志愿,否则撤销刚才的操作

第二问类似,暴力枚举某个人到了前面的哪一名,然后把前面其他人在第一问中的方案的图先建好,然后把对应志愿\(\le s_i\)的导师加入图,如果有那个人能匹配上那么就可以前进到这一名,还要记得撤销.做的时候可以先枚举后面的人到的名次,然后枚举人,这样前面的人的图可以快速建出来.每个人的答案是当前名次-最大的合法名次

实现用的是网络流,因为比较方便退流以及撤销边,开个栈栈序撤销.注意及时撤销

// luogu-judger-enable-o2
//qwqwq
#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=200+10,M=3e5+10;
const db eps=1e-4;
int rd()
{
	int x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
int to[M],nt[M],c[M],hd[N<<1],tot=1;
void add(int x,int y,int z)
{
	++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
	++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
}
void wd(){hd[to[tot^1]]=nt[tot],--tot;}
int stk[M<<3][2],tp;
void wdflow()
{
	while(tp)
	{
		int i=stk[tp][0],dt=stk[tp][1];
		c[i]+=dt,c[i^1]-=dt;
		--tp;
	}
}
int ps,pt,lv[N<<1];
queue<int> q;
bool bfs()
{
	for(int i=0;i<=pt;++i) lv[i]=0;
	lv[ps]=1,q.push(ps);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=hd[x];i;i=nt[i])
		{
			int y=to[i];
			if(c[i]>0&&!lv[y])
			{
				lv[y]=lv[x]+1;
				q.push(y);
			}
		}
	}
	return lv[pt];
}
int dfs(int x,int fw)
{
	if(x==pt) return fw;
	int an=0;
	for(int i=hd[x];i;i=nt[i])
	{
		int y=to[i];
		if(c[i]>0&&lv[y]==lv[x]+1)
		{
			int dt=dfs(y,min(fw,c[i]));
			stk[++tp][0]=i,stk[tp][1]=dt;
			c[i]-=dt,c[i^1]+=dt;
			fw-=dt,an+=dt;
			if(!fw) break;
		}
	}
	return an;
}
int dinic()
{
	int an=0,dt;
	while(bfs())
		while((dt=dfs(ps,2333333)))
			an+=dt;
	return an;
}
int n,m,an[N],a2[N],b[N],s[N];
vector<int> dls[N][N];
vector<int>::iterator it;

int main()
{
    int T=rd();
	rd();
	while(T--)
	{
		n=rd(),m=rd();
		ps=0,pt=n+m+1;
		for(int i=0;i<=pt;++i) hd[i]=0;
		tot=1;
		for(int i=1;i<=n;++i)
		{
			add(ps,i+m,1);
			for(int j=1;j<=m+1;++j) dls[i][j].clear();
		}
		for(int i=1;i<=m;++i) add(i,pt,b[i]=rd());
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				dls[i][rd()].push_back(j);
		for(int i=1;i<=n;++i)
		{
			an[i]=m+1;
			for(int j=1;j<=m;++j)
			{
				if(!dls[i][j].size()) continue;
				int latt=tot;
				for(it=dls[i][j].begin();it!=dls[i][j].end();++it)
					add(i+m,*it,1);
				tp=0;
				if(dinic()){an[i]=j;break;}
				wdflow();
				while(tot>latt) wd();
			}
			printf("%d ",an[i]);
		}
		puts("");
		for(int i=1;i<=n;++i) s[i]=rd();
		for(int i=0;i<=pt;++i) hd[i]=0;
		tot=1;
		for(int i=1;i<=n;++i) add(ps,i+m,1),a2[i]=0;
		for(int i=1;i<=m;++i) add(i,pt,b[i]);
		for(int i=1;i<=n;++i)
		{
			for(int j=i;j<=n;++j)
			{
				if(an[j]<=s[j]){a2[j]=j;continue;}
				int latt=tot;
				for(int k=1;k<=s[j];++k)
				{
					if(!dls[j][k].size()) continue;
					for(it=dls[j][k].begin();it!=dls[j][k].end();++it)
						add(j+m,*it,1);
				}
				tp=0;
				if(dinic()) a2[j]=i;
				wdflow();
				while(tot>latt) wd();
			}
			for(it=dls[i][an[i]].begin();it!=dls[i][an[i]].end();++it)
				add(i+m,*it,1);
			tp=0,dinic();
		}
		for(int i=1;i<=n;++i) printf("%d ",i-a2[i]);
		puts("");
	}
	return 0; 
}
posted @ 2019-09-07 10:45  ✡smy✡  阅读(190)  评论(0编辑  收藏  举报