题目:卡牌配对

传送门:None

题目大意:有$n_1$张$X$类牌和$n_2$张$Y$类类牌,每张卡牌上有三个属性值:$A,B,C$。两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。每张卡牌只能用一次,最大化匹配上的卡牌组数。

分析:

做法一:直接上二分图匹配,然后TLE

做法二:只有三个属性值,又存在至多一项属性值使得两张卡牌该项属性值互质

等价于两张卡牌属性$A,B$均不互质,或属性$A,C$均不互质,或属性$B,C$均不互质

等价于两张卡牌属性$A$有共同因子aa、属性$B$有共同因子$bb$,或属性$A$有共同因子$aa$、属性$C$有共同因子$cc$,或属性$B$有共同因子$bb$、属性$C$有共同因子$cc$,

等价于两张卡牌属性$A$有共同质因子a、属性$B$有共同质因子$b$,或属性$A$有共同质因子$a$、属性$C$有共同质因子$c$,或属性$B$有共同质因子$b$、属性$C$有共同质因子$c$,

这样我们在中间新建一列点$(a,b)$代表属性$A$可以被$a$整除,属性$B$可以被$b$整除,这样连向$(a,b)$的$X$类点和$Y$类点就一定会匹配

同样的,我们建立中间节点$(a,c)$和$(b,c)$

由于200以内有46个质数,这样会增加$46*46*3$个节点,共需要$46*46*3+n1+n2+2$个节点

源点(节点编号:$46*46+n1+n2$)向$X$类节点(节点编号:$46*46*3+i, {i}\in{[0,n_1)}$)建一条流量为1的边

枚举中间节点,如果可以连边,$X$类节点向中间节点建一条流量为$1$的边

枚举中间节点,如果可以连边,中间节点向$Y$类节点(节点编号:$46*46*3+n1+i,{i}\in{[0,n_2)}$)建一条流量为$1$的边,

$Y$类节点向汇点(节点编号:$46*46*3+n1+n2+1$)建一条流量为$1$的边

由于$2*3*5*7>200$,$XY$类节点至多向中间节点连$3*3*3$条边,

这样会有大概$(n1+n2)*3*3*3 + n1 + n2$条边.

枚举中间节点会花费大量时间:$(n1 + n2) * 46 * 46 * 3$ ; TLE警告

做法三:

考虑到属性值小于200,我们可以预处理每一个$(a,b)$可以连出去的中间点编号,$(a,c)$,$(b,c)$加上$46*46$,$46*46*2$就好啦

预处理时间:$200 * 200 * 46 * 46$

考虑到大量属性值点对可能不会出现,于是采用类似记忆化的做法加速

每个属性点可以先预处理出他的质因数,这样可以再优化一下

这道题节点数和边数非常的多,然后“请大胆使用渐进复杂度较高的做法”是通过本题的关键 = =

代码:

 #include <bits/stdc++.h>
using namespace std;
const int MAXN = ;
const int MAXM = ;
const int INF = 1e9 + ;
typedef int LL;
int pn;
vector <int> prime, markp[], mark[][];
void init() {
for(int i = ; i <= ; i++) {
int fg = ;
for(int j = ; j < i; j++)
if(i % j == ) fg = ;
if(fg) prime.push_back(i);
}
pn = prime.size();
for(int i = ; i <= ; i++)
for(int j = ; j < (int)prime.size(); j++)
if(i % prime[j] == ) markp[i].push_back(j);
}
void getMark(int a, int b) {
for(auto i : markp[a])
for(auto j : markp[b])
if(a % prime[i] == && b % prime[j] == )
mark[a][b].push_back(i * pn + j);
}
namespace NWF {
struct Edge {
int to, nxt;LL f;
} e[MAXM << ];
int S, T, tot;
int ecnt, head[MAXN], cur[MAXN], dis[MAXN];
queue<int> q;
void init(int _S, int _T, int _tot){
ecnt = ; S = _S; T = _T; tot = _tot;
memset(head, , (tot + ) * sizeof(int));
}
void addEdge(int u, int v, LL f) {
e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt;
e[++ecnt] = (Edge) {u, head[v], }; head[v] = ecnt;
}
bool bfs() {
memset(dis, , (tot + ) * sizeof(int));
q.push(S); dis[S] = ;
while (!q.empty()) {
int u = q.front(), v; q.pop();
for (int i = cur[u] = head[u]; i ; i = e[i].nxt) {
if (e[i].f && !dis[v = e[i].to]) {
q.push(v);
dis[v] = dis[u] + ;
}
}
}
return dis[T];
}
LL dfs(int u, LL maxf) {
if (u == T) return maxf;
LL sumf = maxf;
for (int &i = cur[u]; i; i = e[i].nxt) {
if (e[i].f && dis[e[i].to] > dis[u]) {
LL tmpf = dfs(e[i].to, min(sumf, e[i].f));
e[i].f -= tmpf; e[i ^ ].f += tmpf;
sumf -= tmpf;
if (!sumf) return maxf;
}
}
return maxf - sumf;
}
LL dinic() {
LL ret = ;
while (bfs()) ret += dfs(S, INF);
return ret;
}
}
int main() {
freopen("4205_19.in","r",stdin);
init();
int n1, n2;
scanf("%d%d",&n1,&n2);
int n = n1 + n2;
NWF::init(pn *pn * + n, pn * pn * + n + , pn * pn * + n + );
for(int k = ,a,b,c; k < n1; k++) {
scanf("%d%d%d",&a,&b,&c);
int id = pn * pn * + k;
NWF::addEdge(NWF::S, id, );
if(mark[a][b].size() == && a != && b != ) getMark(a, b);
if(mark[a][c].size() == && a != && c != ) getMark(a, c);
if(mark[b][c].size() == && b != && c != ) getMark(b, c);
for(auto it : mark[a][b]) NWF::addEdge(id, it, );
for(auto it : mark[a][c]) NWF::addEdge(id, pn*pn+it, );
for(auto it : mark[b][c]) NWF::addEdge(id, *pn*pn+it, );
}
for(int k = ,a,b,c; k < n2; k++) {
scanf("%d%d%d",&a,&b,&c);
int id = pn * pn * + n1 + k;
NWF::addEdge(id, NWF::T, );
if(mark[a][b].size() == && a != && b != ) getMark(a, b);
if(mark[a][c].size() == && a != && c != ) getMark(a, c);
if(mark[b][c].size() == && b != && c != ) getMark(b, c);
for(auto it : mark[a][b]) NWF::addEdge(it, id, );
for(auto it : mark[a][c]) NWF::addEdge(pn*pn+it, id, );
for(auto it : mark[b][c]) NWF::addEdge(*pn*pn+it, id, );
}
int ans = NWF::dinic();
printf("%d\n",ans);
return ;
}

dinic

 #include <bits/stdc++.h>
using namespace std;
const int MAXN = ;
const int MAXM = ;
const int INF = 1e9 + ;
typedef int LL;
int pn;
vector <int> prime, markp[], mark[][];
void init() {
for(int i = ; i <= ; i++) {
int fg = ;
for(int j = ; j < i; j++)
if(i % j == ) fg = ;
if(fg) prime.push_back(i);
}
pn = prime.size();
for(int i = ; i <= ; i++)
for(int j = ; j < (int)prime.size(); j++)
if(i % prime[j] == ) markp[i].push_back(j);
}
void getMark(int a, int b) {
for(auto i : markp[a])
for(auto j : markp[b])
if(a % prime[i] == && b % prime[j] == )
mark[a][b].push_back(i * pn + j);
}
namespace NWF {
struct Edge{
int to, nxt;LL f;
}e[MAXM << ];
int S, T, tot;
int ecnt, head[MAXN], cur[MAXN], pre[MAXN], num[MAXN], dis[MAXN];
queue<int> q;
void init(int _S, int _T, int _tot){
ecnt = ; S = _S; T = _T; tot = _tot;
memset(num, , (tot + ) * sizeof(int));
memset(head, , (tot + ) * sizeof(int));
}
inline void addEdge(int u, int v, LL f) {
e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt;
e[++ecnt] = (Edge) {u, head[v], }; head[v] = ecnt;
}
void bfs() {
memset(dis, , (tot + ) * sizeof(int));
q.push(T);
dis[T] = ;
while(!q.empty()) {
int u = q.front(), v; q.pop();
num[dis[u]]++;
for(int i = cur[u] = head[u]; i; i = e[i].nxt) {
if(!dis[v = e[i].to]) {
dis[v] = dis[u] + ;
q.push(v);
}
}
}
}
LL augment() {
LL flow = INF;
for(int i = S; i != T; i = e[cur[i]].to)
flow = min(flow, e[cur[i]].f);
for(int i = S; i != T; i = e[cur[i]].to) {
e[cur[i]].f -= flow;
e[cur[i] ^ ].f += flow;
}
return flow;
}
LL isap() {
bfs();
int u = S, v;
LL flow = ;
while(dis[S] <= tot) {
if(u == T) {
flow += augment();
u = S;
}
bool fg = ;
for(int i = cur[u]; i; i = e[i].nxt) {
if(e[i].f && dis[u] > dis[v = e[i].to]) {
pre[v] = u;
cur[u] = i;
u = v;
fg = ;
break;
}
}
if(fg) continue;
if(!--num[dis[u]]) break;
int maxDis = tot;
for(int i = head[u]; i; i = e[i].nxt) {
if(e[i].f && maxDis > dis[v = e[i].to]) {
maxDis = dis[v];
cur[u] = i;
}
}
num[dis[u] = maxDis + ]++;
if(u != S) u = pre[u];
}
return flow;
}
}
int main() {
init();
int n1, n2;
scanf("%d%d",&n1,&n2);
int n = n1 + n2;
NWF::init(pn *pn * + n, pn * pn * + n + , pn * pn * + n + );
for(int k = ,a,b,c; k < n1; k++) {
scanf("%d%d%d",&a,&b,&c);
int id = pn * pn * + k;
NWF::addEdge(NWF::S, id, );
if(mark[a][b].size() == && a != && b != ) getMark(a, b);
if(mark[a][c].size() == && a != && c != ) getMark(a, c);
if(mark[b][c].size() == && b != && c != ) getMark(b, c);
for(auto it : mark[a][b]) NWF::addEdge(id, it, );
for(auto it : mark[a][c]) NWF::addEdge(id, pn*pn+it, );
for(auto it : mark[b][c]) NWF::addEdge(id, *pn*pn+it, );
}
for(int k = ,a,b,c; k < n2; k++) {
scanf("%d%d%d",&a,&b,&c);
int id = pn * pn * + n1 + k;
NWF::addEdge(id, NWF::T, );
if(mark[a][b].size() == && a != && b != ) getMark(a, b);
if(mark[a][c].size() == && a != && c != ) getMark(a, c);
if(mark[b][c].size() == && b != && c != ) getMark(b, c);
for(auto it : mark[a][b]) NWF::addEdge(it, id, );
for(auto it : mark[a][c]) NWF::addEdge(pn*pn+it, id, );
for(auto it : mark[b][c]) NWF::addEdge(*pn*pn+it, id, );
}
int ans = NWF::isap();
printf("%d\n",ans);
return ;
}

[BZOJ4205][FJ2015集训]卡牌配对的更多相关文章

  1. [BZOJ4205][FJ2015集训] 卡牌配对 [建图+最大流]

    题面 这是bzoj权限题,题面可以去下面的离线题库找 离线4205,只有题面,不能提交 思路 二分图匹配 这道题模型显然就是个二分图匹配嘛 那我们两两判断一下然后连边匹配.....就只有30分了 因为 ...

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

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

  3. 【BZOJ4205】卡牌配对

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

  4. BZOJ 4205: 卡牌配对

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

  5. BZOJ4205卡牌配对——最大流+建图优化

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

  6. 刷题总结——卡牌配对(bzoj4205网络流)

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

  7. BZOJ4205 : 卡牌配对

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

  8. Unity3D_(游戏)卡牌01_启动屏界面

      卡牌2D游戏展示 (游戏代码放到  卡牌04_游戏界面  文章最后面~) 游戏项目已托管到github上(里面有个32bit可执行文件) 传送门 规则 开始游戏每张卡牌初始翻开展示 展示几秒后卡牌 ...

  9. 使用UIKit制作卡牌游戏(三)ios游戏篇

    译者: Lao Jiang | 原文作者: Matthijs Hollemans写于2012/07/13 转自朋友Tommy 的翻译,自己只翻译了这第三篇教程. 原文地址: http://www.ra ...

随机推荐

  1. IDEA中解决 git pull 冲突

    0.事先准备.1)把远程仓库的README.md内容改写为bbb(原先为aaa). 2)本地仓库的README.md内容改写为ccc(原先也为aaa). 以此来模仿代码冲突.    1.先commit ...

  2. mysql5.7 修改用户密码

    修改vi /etc/my.cnf,增加skip-grant-tables可以免密码登录mysql use mysql ; update user set authentication_string=P ...

  3. [BZOJ3133] [Baltic2013]ballmachine(树上倍增+堆)

    [BZOJ3133] [Baltic2013]ballmachine(树上倍增+堆) 题面 有一个装球机器,构造可以看作是一棵树.有下面两种操作: 从根放入一个球,只要下方有空位,球会沿着树滚下.如果 ...

  4. 【洛谷 P1879】【[USACO06NOV]玉米田Corn Fields】

    题目: 链接 思路: Q:如何想到是状压DP? A:那是因为(我看了标签)\(1 ≤ M ≤ 12; 1 ≤ N ≤ 12\),\(2 ^ {12}\) 不过才...(Win7计算器使用中)\(409 ...

  5. 增强 Jupyter Notebook的功能

    增强 Jupyter Notebook的功能 Jupyter Notebook 是所有开发者共享工作的神器,它为共享 Notebooks 提供了一种便捷方式:结合文本.代码和图更快捷地将信息传达给受众 ...

  6. hdu 2586 How far away ? ( 离线 LCA , tarjan )

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  7. Linux中设置别名alias永久生效

    现在有个项目目录位于/var/www/html/tp5下 这也是我经常用到的工作目录 为了避免每次进入此目录 都需要输入 cd /var/www/html/tp5 可以加上述命令加入别名 alias ...

  8. vue-cli proxyTable中跨域中pathRewrite 解释

      问:proxyTable 里面的pathRewrite里面的‘^/iclient’:'' 什么意思? 答:用代理, 首先你得有一个标识, 告诉他你这个连接要用代理. 不然的话, 可能你的 html ...

  9. Django之cookie 和session

    ---恢复内容开始--- 一.cookie 前戏.cookie 的由来 由于http协议是无状态的 无法记录用户状态 cookie就是保存在客户端浏览器上的键值对 工作原理:当你登陆成功之后 浏览器会 ...

  10. mailstats - 显示邮件状态信息

    总览 mailstats [-o] [-C cffile] [-f stfile] 描述 mailstats工具显示当前的邮件状态信息. 首先,先显示统计启动时所记录的时间,当然是以ctime(3)所 ...