题解 P4382 【[八省联考2018]劈配】
xtx1092515503 · · 题解
一种时空复杂度均严格与
首先,看到这个问题,一下子就能想到二分图多重匹配的模型——每个选手均可以匹配多个导师,而导师有着匹配人数的上限。
于是我们从小到大枚举每个人,再枚举其所有档志愿,依次判断其该档志愿的边加入图中会不会使最大流量变大即可。
为了避免一些奇奇怪怪的错误,我的写法是加入一档志愿后,若合法,则不退流,直接进行下一个人的运算;否则,就要退流,消除本档志愿的影响。具体而言,因为我们的建图是从
下面考虑第二问。第二问的思路也很简单,在第一问的残量网络上,倒着遍历每个人。当遍历到一个人时,先删去其第一问中连上的所有边,然后再把他前
这样就需要一些操作来保证正确性。具体而言,在删去第一问中的边时,同样,要先退流来消去影响,再删边。然后,为了保证不会有两个第二问的人同时匹配上了,却抢走了第一问的人的流量,我们必须保证任意时刻所有第二问的人中最多只能匹配一个。所以,我们要建立一个伪源点
因为我们不需要像常规做法中一样支持前缀和残量网络的查询,所以空间复杂度是十分优秀的;然而,因为退流次数很多,所以实际消耗时间较常规做法甚至更劣,但是当数据范围更大时,此算法便会显著优于常规做法(因为少了二分以及拷贝前缀和残量网络等操作)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,T_T,C_C,lim[210],acc[210],ide[210],imp[210];
namespace MaxFlow{
const int N=510,M=1000000;
int head[N],cur[N],dep[N],S,T,res;
int bin[M],tp,cnt;//storing the id of edges
int newedge(){return tp?bin[tp--]:cnt++;}//generating the id of an edge
struct node{int to,next,last,val;}edge[M];//using a handwritten list to store edges
int ae(int u,int v,int w){//adding an edge in the back, returning the id
int U=newedge();
edge[U].next=head[u],edge[U].last=-1,edge[U].to=v,edge[U].val=w;
if(head[u]!=-1)edge[head[u]].last=U;head[u]=U;
int V=newedge();
edge[V].next=head[v],edge[V].last=-1,edge[V].to=u,edge[V].val=0;
if(head[v]!=-1)edge[head[v]].last=V;head[v]=V;
return U;//under every circumstance, V equals U+1.
}
void ea(int U){//eliminating edge U
int V=U+1,u=edge[V].to,v=edge[U].to;
if(edge[V].next!=-1)edge[edge[V].next].last=edge[V].last;
if(edge[V].last!=-1)edge[edge[V].last].next=edge[V].next;
if(head[v]==V)head[v]=edge[V].next;
bin[++tp]=V;
if(edge[U].next!=-1)edge[edge[U].next].last=edge[U].last;
if(edge[U].last!=-1)edge[edge[U].last].next=edge[U].next;
if(head[u]==U)head[u]=edge[U].next;
bin[++tp]=U;
}
queue<int>q;
inline bool bfs(){
memset(dep,0,sizeof(dep)),q.push(S),dep[S]=1;
while(!q.empty()){
register int x=q.front();q.pop();
for(register int i=cur[x]=head[x];i!=-1;i=edge[i].next)if(edge[i].val&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to);
}
return dep[T]>0;
}
bool reach;
inline int dfs(int x,int flow){
if(x==T){res+=flow,reach=true;return flow;}
int used=0;
for(register int &i=cur[x];i!=-1;i=edge[i].next){
if(!edge[i].val||dep[edge[i].to]!=dep[x]+1)continue;
register int ff=dfs(edge[i].to,min(edge[i].val,flow-used));
if(ff){edge[i].val-=ff,edge[i^1].val+=ff,used+=ff;if(used==flow)break;}
}
return used;
}
inline void Dinic(int SS,int TT){S=SS,T=TT;while(bfs()){reach=true;while(reach)reach=false,dfs(S,0x3f3f3f3f);}}
}
using MaxFlow::head;
using MaxFlow::Dinic;
using MaxFlow::res;
using MaxFlow::edge;
using MaxFlow::cnt;
using MaxFlow::ae;
using MaxFlow::ea;
using MaxFlow::tp;
vector<int>v[210][210];
int read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
int S,T,s;//s is the pseudo source point.
void compensate(int i){//conpensate for the influence of edge i.
edge[i].val=0,edge[i+1].val=0;//blocking the supplying edge
Dinic(T,edge[i].to);//eliminating the influence of the current operation, using backflowing
edge[i].val=1,edge[i+1].val=0;//restore the supplying edge
}
int e[210][210],sz[210];
int main(){
T_T=read(),C_C=read();
while(T_T--){
n=read(),m=read(),memset(head,-1,sizeof(head)),cnt=res=tp=0,s=n+m+1,S=n+m+2,T=n+m+3;
for(int i=1;i<=m;i++)ae(i+n,T,lim[i]=read());
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int x=read();if(x)v[i][x].push_back(j);}
for(int i=1;i<=n;i++)ide[i]=read();
for(int i=1;i<=n;i++){
acc[i]=m+1,e[i][sz[i]=1]=ae(S,i,1);
for(int j=1;j<=m;j++){
for(auto k:v[i][j])e[i][++sz[i]]=ae(i,n+k,1);
Dinic(S,T);
if(res==i){acc[i]=j;break;}
compensate(e[i][1]);
res=i-1;//reset the answer
while(sz[i]>1)ea(e[i][sz[i]--]);//reset the graph
}
res=i;//forcing the flow to i
}
for(int i=1;i<=n;i++)printf("%d ",acc[i]);puts("");
int sor=ae(S,s,1);
for(int i=n;i>=1;i--){
imp[i]=i;
compensate(e[i][1]),res=i-1;
while(sz[i])ea(e[i][sz[i]--]);
e[i][sz[i]=1]=ae(s,i,1);
for(int j=1;j<=ide[i];j++)for(auto k:v[i][j])e[i][++sz[i]]=ae(i,n+k,1);
while(true){
Dinic(S,T);
if(res==i-1){compensate(sor);break;}//inpossible to find one
for(int k=head[s];k!=-1;k=edge[k].next)if(edge[k].to<=n&&!edge[k].val){
int x=edge[k].to;
compensate(k);
imp[x]=x-i,res=i-1;
while(sz[x])ea(e[x][sz[x]--]);
edge[sor].val=1,edge[sor^1].val=0;
break;
}
}
}
for(int i=1;i<=n;i++)printf("%d ",imp[i]);puts("");
for(int i=1;i<=n;i++)sz[i]=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)v[i][j].clear();
}
return 0;
}