题目描述

现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。
两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。
比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。
游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。

输入

数据第一行两个数n1,n2,空格分割。
接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。
接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。

输出

输出一个整数:最多能够匹配的数目。

样例输入

2 2
2 2 2
2 5 5
2 2 5
5 5 5

样例输出

2
【提示】
样例中第一张X类卡牌和第一张Y类卡牌能配对,第二张X类卡牌和两张Y类卡牌都能配对。所以最佳方案是第一张X和第一张Y配对,第二张X和第二张Y配对。
另外,请大胆使用渐进复杂度较高的算法!

提示

对于100%的数据,n1,n2≤ 30000,属性值为不超过200的正整数
 
 

乍一看就是一个简单的二分图最大匹配,枚举任意两个不同类的卡牌连边即可,但光是枚举的时间复杂度就爆炸了,何况还会跑$10^9$条边的最大流。所以我们想办法优化:先考虑两张牌$A,B$属性都分别不互质的情况(剩下两种情况相同),我们找到$A$属性是$x$的倍数、$B$属性是$y$的倍数的所有点,在二分图中间新建一个点然后将左右满足要求的点都连向这个点,流量为$INF$,表示这些点之间都能互相匹配。这里的$x,y$显然只需要枚举质数即可,$200$之内的质数只有$46$个,所以对于一种情况只需要建$46*46$个点即可,总点数为$70000$。而$2*3*5*7>200$,所以每个数最多只有三个质因子,每个点最多连$3*3*3$条边,总边数为$2000000$,直接跑$dinic$即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int head[70000];
int to[4000000];
int next[4000000];
int val[4000000];
int d[70000];
int q[70000];
int back[70000];
int S,T;
int n,m;
int tot=1;
int ans;
int cnt;
vector<int>v[210];
int s[210][210];
int a1[40000];
int b1[40000];
int c1[40000];
int a2[40000];
int b2[40000];
int c2[40000];
int prime[50];
int vis[210];
int num;
void find()
{
for(int i=2;i<=200;i++)
{
if(!vis[i])
{
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&prime[j]*i<=200;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
break;
}
}
}
for(int i=1;i<=200;i++)
{
for(int j=1;j<=cnt;j++)
{
if(i%prime[j]==0)
{
v[i].push_back(j);
}
}
}
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++)
{
s[i][j]=++num;
}
}
}
void add(int x,int y,int v)
{
tot++;
next[tot]=back[x];
back[x]=tot;
to[tot]=y;
val[tot]=v;
tot++;
next[tot]=back[y];
back[y]=tot;
to[tot]=x;
val[tot]=0;
}
bool bfs(int S,int T)
{
int r=0;
int l=0;
memset(d,-1,sizeof(d));
q[r++]=T;
d[T]=2;
while(l<r)
{
int now=q[l];
for(int i=back[now];i;i=next[i])
{
if(d[to[i]]==-1&&val[i^1]!=0)
{
d[to[i]]=d[now]+1;
q[r++]=to[i];
}
}
l++;
}
if(d[S]==-1)
{
return false;
}
else
{
return true;
}
}
int dfs(int x,int flow)
{
if(x==T)
{
return flow;
}
int now_flow;
int used=0;
for(int &i=head[x];i;i=next[i])
{
if(d[to[i]]==d[x]-1&&val[i]!=0)
{
now_flow=dfs(to[i],min(flow-used,val[i]));
val[i]-=now_flow;
val[i^1]+=now_flow;
used+=now_flow;
if(now_flow==flow)
{
return flow;
}
}
}
if(used==0)
{
d[x]=-1;
}
return used;
}
int dinic()
{
int res=0;
while(bfs(S,T))
{
memcpy(head,back,sizeof(back));
res+=dfs(S,0x3f3f3f3f);
}
return res;
}
int main()
{
find();
scanf("%d%d",&n,&m);
S=n+m+46*46*3+1;
T=S+1;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a1[i],&b1[i],&c1[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a2[i],&b2[i],&c2[i]);
}
for(int i=1;i<=n;i++)
{
add(S,i,1);
int sz1=v[a1[i]].size();
int sz2=v[b1[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(i,n+m+s[v[a1[i]][j]][v[b1[i]][k]],INF);
}
}
sz2=v[c1[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(i,n+m+46*46+s[v[a1[i]][j]][v[c1[i]][k]],INF);
}
}
sz1=v[b1[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(i,n+m+2*46*46+s[v[b1[i]][j]][v[c1[i]][k]],INF);
}
}
}
for(int i=1;i<=m;i++)
{
add(n+i,T,1);
int sz1=v[a2[i]].size();
int sz2=v[b2[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(n+m+s[v[a2[i]][j]][v[b2[i]][k]],n+i,INF);
}
}
sz2=v[c2[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(n+m+46*46+s[v[a2[i]][j]][v[c2[i]][k]],n+i,INF);
}
}
sz1=v[b2[i]].size();
for(int j=0;j<sz1;j++)
{
for(int k=0;k<sz2;k++)
{
add(n+m+2*46*46+s[v[b2[i]][j]][v[c2[i]][k]],n+i,INF);
}
}
}
printf("%d",dinic());
}

BZOJ4205卡牌配对——最大流+建图优化的更多相关文章

  1. 【BZOJ4205】卡牌配对 最大流

    [BZOJ4205]卡牌配对 Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值 ...

  2. BZOJ4205 : 卡牌配对

    对于两张卡牌,如果存在两种属性值不互质,则可以匹配. 只考虑200以内的质数,一共有46个,可以新建3*46*46个点来表示一类属性值中有这两种质数的卡牌. 然后对于每张卡牌,枚举它的质因子,最多只有 ...

  3. 【BZOJ4205】卡牌配对

    Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且 ...

  4. [BZOJ4205][FJ2015集训]卡牌配对

    题目:卡牌配对 传送门:None 题目大意:有$n_1$张$X$类牌和$n_2$张$Y$类类牌,每张卡牌上有三个属性值:$A,B,C$.两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属 ...

  5. BZOJ 4205: 卡牌配对

    4205: 卡牌配对 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 173  Solved: 76[Submit][Status][Discuss] ...

  6. poj 3281 最大流+建图

    很巧妙的思想 转自:http://www.cnblogs.com/kuangbin/archive/2012/08/21/2649850.html 本题能够想到用最大流做,那真的是太绝了.建模的方法很 ...

  7. hdu4106 区间k覆盖问题(连续m个数,最多选k个数) 最小费用最大流 建图巧妙

    /** 题目:hdu4106 区间k覆盖问题(连续m个数,最多选k个数) 最小费用最大流 建图巧妙 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4106 ...

  8. poj3680 Intervals 区间k覆盖问题 最小费用最大流 建图巧妙

    /** 题目:poj3680 Intervals 区间k覆盖问题 最小费用最大流 建图巧妙 链接:http://poj.org/problem?id=3680 题意:给定n个区间,每个区间(ai,bi ...

  9. 图论--网络流--最小割 HDU 2485 Destroying the bus stations(最短路+限流建图)

    Problem Description Gabiluso is one of the greatest spies in his country. Now he's trying to complet ...

随机推荐

  1. 【C#复习总结】垃圾回收机制(GC)1

    摘要:今天我们漫谈C#中的垃圾回收机制,本文将从垃圾回收机制的原理讲起,希望对大家有所帮助. GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由 ...

  2. rabbitMQ教程(五)rabbitmq 指令 以及解决web管理界面无法使用guest用户登录

    安装最新版本的rabbitmq(3.3.1),并启用management plugin后,使用默认的账号guest登陆管理控制台,却提示登陆失败. 翻看官方的release文档后,得知由于账号gues ...

  3. 使用 OpenSSL 创建私有 CA:1 根证书

    OpenSSL 创建私有 CA 三部曲:使用 OpenSSL 创建私有 CA:1 根证书使用 OpenSSL 创建私有 CA:2 中间证书使用 OpenSSL 创建私有 CA:3 用户证书 OpenS ...

  4. 模块的语法 import ,from...import....

    ------------------------积极的人在每一次忧患中都看到一个机会, 而消极的人则在每个机会都看到某种忧患 1. 认识模块 模块可以认为是一个py文件. 模块实际上是我们的py文件运 ...

  5. Linux 下RPM打包制作流程

    原文地址:https://www.cnblogs.com/postgres/p/5726339.html 开始前的准备 安装rpmbuild软件包 yum -y install rpm-build 生 ...

  6. Day7 Ubantu学习(一)

    Linux是多用户操作系统 Ubantu学习参考网址:https://www.cnblogs.com/resn/p/5800922.html 1.虚拟机网络类型的理解 bridged(桥接模式) : ...

  7. springboot 双数据源+aop动态切换

    # springboot-double-dataspringboot-double-data 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 ...

  8. stark组件之pop页面,按钮,url,页面

      1.Window open() 方法 2.admin的pop添加按钮 3.stark之pop功能 3.知识点总结 4.coding代码 1.Window open() 方法 效果图   2.adm ...

  9. 【学亮IT手记】Ajax跨域问题精讲--jQuery解决跨域操作

    什么是跨域 跨域,它是不同的域名(服务器)之间的相互的资源之间的访问. 当协议,域名,端口号任意一个不同,它们就是不同的域. 正常情况下,因为浏览器安全的问题,不同域之间的资源是不可以访问的. 跨域的 ...

  10. 五句话搞定JavaScript作用域(ES5)

    JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...