[八省联考2018]劈配
[八省联考2018]劈配
心路历程
这题目,乍一眼看上去像一个DP,因为这个数据范围一般都是DP,但是考虑到转移有后效性,所以显然是做不了DP的。考试的时候像着怎么设状态没有后效性,失败了,就只打了一个70分的暴力,还挂了20分。
解法
这题其实是个(网络流/匹配)(真心看不出,然后我写博客的时候旁边YL神犇看见了,嘲讽了我一波,我是真的弱),首先我们建图,将所有导师向汇点连一条流量为招收学员上限的边,然后就可以开始虐题(被题虐)了,我们按次序枚举每一个学员的志愿,从原点向学员连流量为1的边,按当前枚举到的志愿由学员向导师连边,流量为1,然后开始增广,若失败,则删去这一志愿连的边,继续枚举下一志愿,若成功则这就是第一问答案,这样的话,第\(i\)张图就会是满足前\(i\)个人志愿的剩余图,这在第二问会用到,所以我们要保留下来。
然后第二问,同样是对学员一个一个处理,二分他需要前进的名次,然后直接使用原来的剩余图进行加边,我们可以把他前\(s_i\)的志愿全部一起加上(这显然是对的,一起做可以节省大量时间),然后开始增广,同样的,成功说明他可以少前进一点,失败则需要前进更多。
完整代码
#include<bits/stdc++.h>
#define il inline
using namespace std;
const int _=211;
queue<int>q;
int s[_],n,m,fz[_][_][_],num[_][_];
struct Graph{
int a[_*2],to[_*30],nex[_*30],w[_*30],t,dis[_*2],ans;
il void add(int u,int v,int W){
nex[++t]=a[u];to[t]=v;w[t]=W;a[u]=t;
nex[++t]=a[v];to[t]=u;w[t]=0;a[v]=t;
}
il int bfs(){
int f=0;
memset(dis,0,sizeof(dis));
dis[0]=1;
while(!q.empty())q.pop();
q.push(0);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=a[u];i;i=nex[i]){
int v=to[i];
if(!dis[v]&&w[i]){
dis[v]=dis[u]+1;
if(v==401)f=1;
q.push(v);
}
}
}
return f;
}
il int dfs(int u,int ma){
if(u==401){ans+=ma;return ma;}
for(int i=a[u];i;i=nex[i]){
int v=to[i];
if(dis[v]==dis[u]+1&&w[i]){
int x=dfs(v,min(w[i],ma));
if(x){
w[i]-=x;w[i^1]+=x;
return x;
}
}
}
return 0;
}
il int Dinic(){
int f=0;
if(bfs()){dfs(0,1e9);f=1;}
return f;
}
}G[_];
il int check(int p,int i){
G[201]=G[p-1];
G[201].add(0,i,1);
for(int j=1;j<=s[i];++j){
for(int k=1;k<=num[i][j];++k){
G[201].add(i,fz[i][j][k],1);
}
}
return G[201].Dinic();
}
int main(){
int T,C;
cin>>T>>C;
while(T--){
memset(G,0,sizeof(G));
memset(s,0,sizeof(s));
memset(fz,0,sizeof(fz));
memset(num,0,sizeof(num));
n=0,m=0;
G[0].t=1;
cin>>n>>m;
for(int i=1;i<=m;++i){
int x;
scanf("%d",&x);
G[0].add(i+200,401,x);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
int x;
scanf("%d",&x);
fz[i][x][++num[i][x]]=j+200;
}
for(int i=1;i<=n;++i)
scanf("%d",&s[i]);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
G[i]=G[i-1];G[i].t=G[i-1].t;
G[i].add(0,i,1);
for(int k=1;k<=num[i][j];++k){
G[i].add(i,fz[i][j][k],1);
}
if(G[i].Dinic()){
printf("%d ",j);
break;
}
if(j==m){printf("%d ",m+1);}
}
}
puts("");
for(int i=1;i<=n;++i){
int l=0,r=i;
while(l!=r){
int mid=(l+r)>>1;
if(check(i-mid,i))r=mid;
else l=mid+1;
}
printf("%d ",l);
}
puts("");
}
}