题目:卡牌配对
解析:
妙啊!这建图,这操作,令人望尘莫及!
回到正题。
首先网络流二分图匹配是很明显的,但是建图只有60分。
然后我们考虑要使一个流是合法流,那么一定满足这两个数的三个属性值中至少有两个不是互质的,即有公共质因子。于是我们可以这样建图,考虑 A 项属性值能被 x 整除且 B 项能力值能被 y 整除的所有点(x,y),只要是在两侧一定能够匹配,所以我们在匹配的网络流模型中间增加一排这样的点,满足要求的左右点分别与它相连,边权为正无穷。
建图分析:
考虑到x和y只需是质数,这样的点共有至多3*46*46个(1~200质数共46个),而200<2*3*5*7,所以两侧每个点至多连出3*3*3条边。于是我们构成了一个70000个点,2000000条边的网络流,依然是分层图,所以dinic有极佳的速度优势,通过100分数据。 -------hzwer
代码:
#include <bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int Maxn=70005;
const int Maxm=4000010;
int n,m,size=1,tot,S,T,ans;
int vis[205],id[50][50],pri[50],first[Maxn],tmp[Maxn],dep[Maxn],g[Maxn];
vector<int>q[205];
struct shu{int to,next,len;}edge[Maxm];
struct in{int x,y,z;}a[30010],b[30010];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void pre()
{
for(int i=2;i<=200;i++)
{
if(vis[i]) continue;
for(int j=i;j<=200/i;j++) vis[i*j]=1;
}
for(int i=2;i<=200;i++) if(!vis[i]) pri[++tot]=i;
for(int i=2;i<=200;i++)
for(int j=1;j<=tot;j++)
if(!(i%pri[j])) q[i].push_back(j);
tot=0;
for(int i=1;i<=46;i++)
for(int j=1;j<=46;j++) id[i][j]=++tot;
}
inline void insert(int x,int y,int z)
{
edge[++size].next=first[x],first[x]=size,edge[size].to=y,edge[size].len=z;
edge[++size].next=first[y],first[y]=size,edge[size].to=x,edge[size].len=0;
}
inline void build(int p,int tag)
{
int x,y,z;
if(!tag) x=a[p].x,y=a[p].y,z=a[p].z;
else x=b[p].x,y=b[p].y,z=b[p].z;
for(int i=0;i<q[x].size();i++)
for(int j=0;j<q[y].size();j++)
if(!tag) insert(p,id[q[x][i]][q[y][j]]+n+m,1);
else insert(id[q[x][i]][q[y][j]]+n+m,n+p,1);
for(int i=0;i<q[x].size();i++)
for(int j=0;j<q[z].size();j++)
if(!tag) insert(p,id[q[x][i]][q[z][j]]+n+m+46*46,1);
else insert(id[q[x][i]][q[z][j]]+n+m+46*46,n+p,1);
for(int i=0;i<q[y].size();i++)
for(int j=0;j<q[z].size();j++)
if(!tag) insert(p,id[q[y][i]][q[z][j]]+n+m+46*46*2,1);
else insert(id[q[y][i]][q[z][j]]+n+m+46*46*2,n+p,1);
}
inline bool bfs()
{
memset(dep,0,sizeof(dep));
queue<int>g;g.push(S),dep[S]=1;
while(g.size())
{
int p=g.front();g.pop();
for(int u=first[p];u;u=edge[u].next)
{
int to=edge[u].to;
if(!dep[to]&&edge[u].len)
{
dep[to]=dep[p]+1,g.push(to);
if(to==T) return 1;
}
}
}
return 0;
}
inline int dinic(int p,int flow)
{
if(p==T) return flow;
int sum=0;
for(int &u=tmp[p];u&&sum<flow;u=edge[u].next)
{
int to=edge[u].to;
if(dep[to]==dep[p]+1&&edge[u].len)
{
int minn=dinic(to,min(flow-sum,edge[u].len));
edge[u].len-=minn,edge[u^1].len+=minn,sum+=minn;
if(flow==sum) return sum;
}
}
return dep[p]=0,sum;
}
inline void solve()
{
while(bfs())
{
memcpy(tmp,first,sizeof(first));
ans+=dinic(S,inf);
}
}
int main()
{
pre();
n=get_int(),m=get_int(),T=n+m+46*46*3+1;
for(int i=1;i<=n;i++) a[i].x=get_int(),a[i].y=get_int(),a[i].z=get_int();
for(int i=1;i<=m;i++) b[i].x=get_int(),b[i].y=get_int(),b[i].z=get_int();
for(int i=1;i<=n;i++) insert(S,i,1),build(i,0);
for(int i=1;i<=m;i++) insert(i+n,T,1),build(i,1);
solve();
cout<<ans;
return 0;
}