Codeforces 1483A/1484C - Basic Diplomacy (网络流)
Codeforces Round #709 (Div. 1, based on Technocup 2021 Final Round) A. Basic Diplomacy
Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round) C. Basic Diplomacy
题意
Aleksey有\(n\)个朋友以及\(m\)天假期
每一天他都想和一个朋友一起玩
第\(i\)天有\(k_i\)个朋友有时间跟他玩
如果有某个朋友在这\(m\)天中被邀请超过\(\lceil \frac m 2 \rceil\)次,其他朋友会觉得被冒犯到
为了不让任何一个朋友感觉被冒犯,试找到一个合理方案
限制
\(1\le T\le 10^4\)
\(1\le n,m\le 10^5\)
\(1\le k_i\le n\)
\(\sum n\le 10^5,\ \sum m\le 10^5,\ \sum k_i \le 2\cdot 10^5\)
思路
赛事直接开C,明显就是道类二分图匹配,没多想直接开网络流莽过去了,所以 这篇题解只讨论网络流解法
提出题意关键词:每一天选一个朋友,每天只有\(k_i\)个朋友可选,每个朋友最多选\(\lceil \frac m 2 \rceil\)次
故可以将每一天看作二分图左侧点,每个朋友看作二分图右侧点
并让每一天编号为\(1\sim m\),每个朋友编号\(m+1\sim m+n\)
每一天只能选一个朋友,可以让源点向每一天连一条流量为\(1\)的边来限制
每天只有\(k_i\)个朋友可选,说明每天可以向\(k_i\)个不同的朋友连一条流量为\(1\)的边
每个朋友最多选\(\lceil \frac m 2 \rceil\)次,故每个朋友向汇点连一条流量为\(\lceil \frac m 2 \rceil\)的边来限制
建图完毕后直接跑最大流,如果最大流量为\(m\),则表示\(m\)天均匹配到了一个朋友,否则直接输出\(NO\)
然后在残量网络上寻找有向边\((u,v)\),如果这条边连接的是(天->朋友)且流量不为\(0\),则说明第\(u\)天匹配第\(v\)个朋友,最后输出即可
代码
(670ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100050,maxm=400050;
const int INF=0x3f3f3f3f;
struct edge{
int u,v,cap,flow;
edge(){}
edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];
void init(int n){
tot=0;
while(n)tab[n--].clear();
}
void addedge(int u,int v,int cap){
tab[u].push_back(tot);
eg[tot++]=edge(u,v,cap,0);
tab[v].push_back(tot);
eg[tot++]=edge(v,u,0,0);
}
int bfs(int s,int t){
queue<int> q;
q.push(s);
memset(dis,INF,sizeof dis);
dis[s]=0;
while(!q.empty()){
int h=q.front(),i;
q.pop();
for(i=0;i<tab[h].size();i++){
edge &e=eg[tab[h][i]];
if(e.cap>e.flow&&dis[e.v]==INF){
dis[e.v]=dis[h]+1;
q.push(e.v);
}
}
}
return dis[t]<INF;
}
int dfs(int x,int maxflow,int s,int t){
if(x==t||maxflow==0)
return maxflow;
int flow=0,i,f;
for(i=cur[x];i<tab[x].size();i++){
cur[x]=i;
edge &e=eg[tab[x][i]];
if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow),s,t))>0){
e.flow+=f;
eg[tab[x][i]^1].flow-=f;
flow+=f;
maxflow-=f;
if(maxflow==0)
break;
}
}
return flow;
}
int dinic(int s,int t){
int flow=0;
while(bfs(s,t)){
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF,s,t);
}
return flow;
}
int ans[maxn];
void solve()
{
int n,m;
cin>>n>>m;
init(m+n+2);
for(int i=1;i<=m;i++)
{
int t;
cin>>t;
while(t--)
{
int j;
cin>>j;
addedge(i,j+m,1); //第i天向第j个朋友连边
}
addedge(n+m+1,i,1); //源点向第i天连边
}
for(int i=1;i<=n;i++)
addedge(i+m,n+m+2,(m+1)/2); //第i个朋友向汇点连边
if(dinic(n+m+1,n+m+2)==m) //m个点全都能完成匹配
{
cout<<"YES\n";
for(int i=0;i<tot;i+=2)
{
if(eg[i].u<=m&&eg[i].flow) //残量网络中如果这条边被使用
ans[eg[i].u]=eg[i].v-m;
}
for(int i=1;i<=m;i++)
cout<<ans[i]<<' ';
cout<<'\n';
}
else
cout<<"NO\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}