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 ceil)次,其他朋友会觉得被冒犯到
为了不让任何一个朋友感觉被冒犯,试找到一个合理方案
限制
(1le Tle 10^4)
(1le n,mle 10^5)
(1le k_ile n)
(sum nle 10^5, sum mle 10^5, sum k_i le 2cdot 10^5)
思路
赛事直接开C,明显就是道类二分图匹配,没多想直接开网络流莽过去了,所以 这篇题解只讨论网络流解法
提出题意关键词:每一天选一个朋友,每天只有(k_i)个朋友可选,每个朋友最多选(lceil frac m 2 ceil)次
故可以将每一天看作二分图左侧点,每个朋友看作二分图右侧点
并让每一天编号为(1sim m),每个朋友编号(m+1sim m+n)
每一天只能选一个朋友,可以让源点向每一天连一条流量为(1)的边来限制
每天只有(k_i)个朋友可选,说明每天可以向(k_i)个不同的朋友连一条流量为(1)的边
每个朋友最多选(lceil frac m 2 ceil)次,故每个朋友向汇点连一条流量为(lceil frac m 2 ceil)的边来限制
建图完毕后直接跑最大流,如果最大流量为(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
";
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<<'
';
}
else
cout<<"NO
";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}