LOJ2477 「九省联考 2018」劈配
这道题目的题意又好绕啊。这种超级长的题面就不能附一个简要题意吗?点名批评 NOIP2021T4 。
一边试错搜索,加入每一个人的每一档,一边用网络流 check 即可解决第一问,(感觉最近网络流算法老是被忘记。。。)
对于第二问,我们考虑也用网络流来试错。具体的,我们处理出前 i 个人选择好的网络流的图。由于每次增广的时候只会拓展 1 ,所以我们可以暴力处理出哪些点可以到达汇点,每次加入一条边的时候直接 check 起点能否到达源点(此题中一定能),终点能否到达汇点。
你会发现此时和第一问是可以一起做的。
这里就用到了每次流量递增一,所以可以用暴力维护出哪些点是可行的,还算是一个比较有用的技巧。在优化二分图匹配的时候尽量不要往网络流的方向去思考,用暴力增广单次的方法往往是比较有效的。
可以再举 pj 的 round1 的 T2 ,也是利用这个性质进行优化的。
#include<bits/stdc++.h>
using namespace std;
const int N=2e2+5,M=2e2+5;
const int INF=1e9+7;
int n,m,b[M],s[N];
vector<int> bag[N][M];
int id[N],ID[M];
struct Dinic{
int tot,from,to;
struct Edge{int nxt,to,flow;}e[N*M*2];int fir[N+M],e_siz;
void add(int u,int v,int w){e[++e_siz]=(Edge){fir[u],v,w},fir[u]=e_siz;}
void init(){
for(int i=1;i<=tot;++i) fir[i]=0;
tot=0,from=++tot,to=++tot,e_siz=1;
}
int cur[N+M],dis[N+M],vis[N+M],q[N+M],h,t;
bool bfs(){
for(int i=1;i<=tot;++i) cur[i]=fir[i],dis[i]=INF;
dis[from]=0,h=1,t=0,q[++t]=from;
while(h<=t){
int u=q[h++];
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;
if(!e[i].flow||dis[v]<=dis[u]+1) continue;
dis[v]=dis[u]+1,q[++t]=v;
}
}
return dis[to]!=INF;
}
int dfs(int u,int flow){
if(u==to) return flow;
int res=0;vis[u]=true;
for(int i=cur[u];i&&flow;i=e[i].nxt){
int v=e[i].to;cur[u]=i;
if(vis[v]||!e[i].flow||dis[v]!=dis[u]+1) continue;
int tmp=dfs(v,min(flow,e[i].flow));
e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
}
return vis[u]=false,res;
}
int Max_Flow(){
int res=0;
while(bfs()) res+=dfs(from,INF);
return res;
}
int tag[N+M];
void get(){
for(int i=1;i<=tot;++i) tag[i]=false;
tag[to]=true,h=1,t=0,q[++t]=to;
while(h<=t){
int u=q[h++];
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;
if(!e[i^1].flow||tag[v]) continue;
tag[v]=true,q[++t]=v;
}
}
}
}mf;
int tag[N][M],res1[N],res2[N];
int solve(){
cin>>n>>m;
for(int i=1;i<=m;++i) scanf("%d",&b[i]);
for(int i=1;i<=n;++i) for(int j=0;j<=m+1;++j) bag[i][j].clear();
for(int i=1;i<=n;++i) for(int j=1,x;j<=m;++j) scanf("%d",&x),bag[i][x].push_back(j);
for(int i=1;i<=n;++i) scanf("%d",&s[i]);
mf.init();
for(int i=1;i<=n;++i) id[i]=++mf.tot;
for(int i=1;i<=m;++i) ID[i]=++mf.tot;
for(int i=1;i<=n;++i) mf.add(mf.from,id[i],1),mf.add(id[i],mf.from,0);
for(int i=1;i<=m;++i) mf.add(ID[i],mf.to,b[i]),mf.add(mf.to,ID[i],0);
for(int i=1;i<=n;++i) res1[i]=m+1,res2[i]=i;
for(int i=1;i<=n;++i){
mf.get();
for(int j=1;j<=m;++j) tag[i][j]=mf.tag[ID[j]];
for(int j=1;j<=m;++j){
bool flag=false;
for(int x:bag[i][j]) flag|=tag[i][x];
if(flag){res1[i]=j;break;}
}
for(int x:bag[i][res1[i]]) mf.add(id[i],ID[x],1),mf.add(ID[x],id[i],0);
mf.Max_Flow();
}
for(int i=1;i<=n;++i){
int L=1,R=i;
while(L<=R){
int Mid=(L+R)>>1;bool flag=false;
for(int j=1;j<=s[i];++j) for(int x:bag[i][j]) flag|=tag[Mid][x];
if(flag) L=Mid+1,res2[i]=i-Mid;else R=Mid-1;
}
}
for(int i=1;i<=n;++i) printf("%d ",res1[i]);
printf("\n");
for(int i=1;i<=n;++i) printf("%d ",res2[i]);
printf("\n");
return 0;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int T,c;cin>>T>>c;while(T--) solve();
return 0;
}