Codeforces Round #290 (Div. 1) C. Fox And Dinner(二分图多重匹配)
Posted 吃花椒的妙酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #290 (Div. 1) C. Fox And Dinner(二分图多重匹配)相关的知识,希望对你有一定的参考价值。
题目大意:n个数,组成若干环,每个环中相邻数的和为质数,输出方案
n<=200,数不超过1e4
思路:二分图多重匹配
和为质数,说明两个是奇+偶的形式,将数分为奇偶两个集合,每个数都有两个邻居,所以与源点(汇点)的边容量为2,和为质数就相互连边,容量1和inf都行
拿3489举个例子,建图如上
然后跑最大流,看是否满流,这里要分别看源点和汇点两边是否满流
求方案的话,dfs一下即可
代码较长,写了点注释
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define pb push_back
#define pii pair<int ,int >
using namespace std;
const int N=1e5+10;
int n,np,nc,m;
int s,t;
int d[N];
int pre[N];
int nxt[N];
int a[N];
struct ty
{
int t,next,flow;
}edge[2*100010];
int tot=1,head[N];
vector<int > odd,even;
void add(int x ,int y, int flow)
{
// cout<<x<<"->"<<y<<" flow "<<flow<<endl;
edge[++tot]={y,head[x],flow};
head[x]=tot;
edge[++tot]={x,head[y],0};
head[y]=tot;
}
void ini()
{
tot=1;
mst(head,-1);
}
bool bfs(int t)
{
mst(d,0);
queue <int> q;
q.push(s);
d[s]=1;
while( !q.empty() )
{
int x = q.front();
q.pop();
if( x==t ) return true;
for(int i=head[x] ;i!=-1;i=edge[i].next)
{
int y = edge[i].t;
if( edge[i].flow > 0 && !d[y] )
{
d[y] = d[x] + 1;
q.push(y);
}
}
}
return false;
}
int dfs(int x ,int flow ,int t)
{
if( x==t ) return flow;
int sum=0;
for(int i=head[x] ;i!=-1;i=edge[i].next)
{
int y = edge[i].t;
if( d[y] == d[x] +1 && edge[i].flow>0 )
{
int temp = dfs(y,min(edge[i].flow , flow-sum),t);
if( temp )//有流就记录路径
{
nxt[x] =y-n;//记录下一步
}
edge[i].flow -= temp;
edge[i^1].flow += temp;
sum += temp;
if( sum == flow ) return flow;
}
}
return sum;
}
int cnt=0;
bool v[N],pd[N];
int p[N];
void shai(int n)//素数筛
{
_for(i,2,n)
{
if( !v[i] )
{
p[++cnt]=i;
}
_for(j,1,cnt)
{
if( p[j]*i > n ) break;
v[p[j]*i]=1;
if(i%p[j]==0 ) break;
}
}
_for(i,1,cnt)
{
pd[p[i]]=1;
}
}
bool vv[N];
vector <int > temp[N];
void dfs2(int x ,int f)//求方案数,流为0的边说明在方案中
{
vv[x]=1;//点被访问过
temp[f].pb(x);//放进以f为首的环
for(int i=head[x] ;i!=-1;i=edge[i].next)
{
int y = edge[i].t;
if( vv[y] ) continue;//点被访问过
if( a[x]%2==1 && edge[i].flow == 0 )//对于奇数来说,左流到右
{
dfs2(y,f);
}
else if( a[x]%2==0 && edge[i^1].flow == 0)//对于偶数来说,要看反边
{
dfs2(y,f);
}
}
}
void print()
{
for(auto u:odd)//以奇数为起点跑dfs
{
if( vv[u] ) continue;
dfs2(u,u);
}
int cnt2=0;
for(int i=1 ;i<=n ;i++)
{
if( !temp[i].empty() ) cnt2++;//有多少个环
}
cout<<cnt2<<endl;
for(int i=1 ;i<=n ;i++)
{
if( temp[i].empty() ) continue;
cout<<temp[i].size()<<" ";
for(auto u:temp[i])
{
cout<<u<<" ";
}
cout<<endl;
}
}
signed main()
{
ini();
shai(2e4);
// freopen("data.txt","r",stdin);
cin>>n;
_for(i,1,n)
{
int x;cin>>x;
a[i] = x;
if( x&1 ) odd.pb(i);
else even.pb(i);
}
int s=0,t=500;
for(auto u:odd)
{
add(s,u,2);
}
for(auto u:even)
{
add(u,t,2);
}
for(auto x:odd)
{
for(auto y:even)
{
if( pd[a[x]+a[y]] )
add(x,y,1);
}
}
int ans=0;
while( bfs(t) ) ans += dfs(s,inf,t);
if( ans != 2*odd.size() || ans != 2*even.size() )//奇数偶数两边判满流
{
cout<<"Impossible"<<endl;
return 0;
}
print();
}
以上是关于Codeforces Round #290 (Div. 1) C. Fox And Dinner(二分图多重匹配)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #290 (Div. 2) B. Fox And Two Dots(DFS)
Codeforces Round #290 (Div. 1) C. Fox And Dinner(二分图多重匹配)
Codeforces Round #436 E. Fire(背包dp+输出路径)
[ACM]Codeforces Round #534 (Div. 2)