劈配,匹配,网络流。那么考虑怎么跑网络流。
先看第一问。首先套路的建出超源超汇。不用想也知道导师向汇连容量为战队人数上限的边。特别地,给出局也建一个点,向汇连容量inf的边(似乎没有必要)。对于一个新学员,假设我们已经知道了之前的学员的最优选择,可以把之前的每名学员和可以选择的导师连边,并由源向学员连容量为1的边。然后对于该名新学员,先只连最优选择的边,如果此时跑出的最大流不等于学员数,则表明这名学员无法选择最优,那么删掉最优边(此时这些边里一定没有流量,可以通过容量改为0实现)并连上次优边,次优边还是不行的话继续,一直这样下去直到最大流等于学员数。第一问就做完了。至于复杂度,O(能过)。
然后是第二问。对于每个人可以二分答案,然后跑最大流看其是否满足。似乎需要访问网络的历史状态?可持久化网络流!这玩意似乎没法可持久化啊……那就暴力记下来呗。由于有C的限制,这里面的边不会很多。于是就做完了,O(能过)。还有一种做法是先连上该学员可以选择的边,然后从第一名开始依次把最优选择加进去跑,直到无法满足,可能会快不少。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<vector> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 210 #define S 0 #define T 404 int test,c,n,m,b[N],s[N],p[N<<1],cnt[N],t; int cur[N<<1],d[N<<1],q[N<<1],ans,cho[N]; vector<int> a[N][N]; struct data{int to,nxt,cap,flow; }edge[N*N<<2],hisedge[N][2510]; void addedge(int x,int y,int z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t; } void addedge(int x,int y,int cap,int flow) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=cap,edge[t].flow=flow,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=-flow,p[y]=t; } void init() { for (int i=1;i<=m;i++) b[i]=read(); for (int i=1;i<=n;i++) { for (int j=1;j<=m+1;j++) a[i][j].clear(); for (int j=1;j<=m;j++) { int x=read(); if (x) a[i][x].push_back(j); } a[i][m+1].push_back(m+1); } for (int i=1;i<=n;i++) s[i]=read(); } bool bfs() { memset(d,255,sizeof(d));d[S]=0; int head=0,tail=1;q[1]=S; do { int x=q[++head]; for (int i=p[x];~i;i=edge[i].nxt) if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+1; q[++tail]=edge[i].to; } }while (head<tail); return ~d[T]; } int work(int k,int f) { if (k==T) return f; int used=0; for (int i=cur[k];~i;i=edge[i].nxt) if (d[k]+1==d[edge[i].to]) { int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow)); edge[i].flow+=w,edge[i^1].flow-=w; if (edge[i].flow<edge[i].cap) cur[k]=i; used+=w;if (used==f) return f; } if (used==0) d[k]=-1; return used; } void dinic() { while (bfs()) { memcpy(cur,p,sizeof(p)); ans+=work(S,N); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5251.in","r",stdin); freopen("bzoj5251.out","w",stdout); const char LL[]="%I64d"; #else const char LL[]="%lld"; #endif test=read(),c=read(); while (test--) { n=read(),m=read(); t=-1; memset(p,255,sizeof(p)); memset(cnt,0,sizeof(cnt)); init(); for (int i=1;i<=m;i++) addedge(n+i,T,b[i]); addedge(n+m+1,T,N); for (int j=0;j<=t;j+=2) if (edge[j].cap) hisedge[0][++cnt[0]]=edge[j],hisedge[0][cnt[0]].nxt=edge[j^1].to; ans=0; for (int i=1;i<=n;i++) { addedge(S,i,1); for (int j=1;j<=m+1;j++) { int tmp=t; for (int k=0;k<a[i][j].size();k++) addedge(i,n+a[i][j][k],1); dinic(); if (ans==i) {cho[i]=j;break;} for (int k=tmp+1;k<=t;k+=2) edge[k].cap=0; } for (int j=0;j<=t;j+=2) if (edge[j].cap) hisedge[i][++cnt[i]]=edge[j],hisedge[i][cnt[i]].nxt=edge[j^1].to; } /*for (int i=0;i<=n;i++) { for (int j=1;j<=cnt[i];j++) cout<<hisedge[i][j].nxt<<' '<<hisedge[i][j].to<<' '<<hisedge[i][j].cap<<' '<<hisedge[i][j].flow<<endl; cout<<endl; }*/ for (int i=1;i<=n;i++) printf("%d ",cho[i]); cout<<endl; for (int i=1;i<=n;i++) { int l=0,r=i-1,add=-1; while (l<=r) { int mid=l+r>>1; memset(p,255,sizeof(p)); t=-1; addedge(S,i,1); for (int j=1;j<=s[i];j++) for (int k=0;k<a[i][j].size();k++) addedge(i,n+a[i][j][k],1); for (int j=1;j<=cnt[mid];j++) addedge(hisedge[mid][j].nxt,hisedge[mid][j].to,hisedge[mid][j].cap,hisedge[mid][j].flow); ans=0; dinic(); if (!ans) r=mid-1; else l=mid+1,add=mid; } printf("%d ",i-add-1); } cout<<endl; } return 0; }