LOJ3051 「十二省聯考 2019」皮配
阿新 • • 發佈:2022-04-12
這道題目的題意又好繞啊。這種超級長的題面就不能附一個簡要題意嗎?點名批評 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; }