[BZOJ4205][FJ2015集训]卡牌配对
题目:卡牌配对
传送门: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集训]卡牌配对的更多相关文章
- [BZOJ4205][FJ2015集训] 卡牌配对 [建图+最大流]
题面 这是bzoj权限题,题面可以去下面的离线题库找 离线4205,只有题面,不能提交 思路 二分图匹配 这道题模型显然就是个二分图匹配嘛 那我们两两判断一下然后连边匹配.....就只有30分了 因为 ...
- 【BZOJ4205】卡牌配对 最大流
[BZOJ4205]卡牌配对 Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值 ...
- 【BZOJ4205】卡牌配对
Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且 ...
- BZOJ 4205: 卡牌配对
4205: 卡牌配对 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 173 Solved: 76[Submit][Status][Discuss] ...
- BZOJ4205卡牌配对——最大流+建图优化
题目描述 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不 ...
- 刷题总结——卡牌配对(bzoj4205网络流)
题目: Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值 ...
- BZOJ4205 : 卡牌配对
对于两张卡牌,如果存在两种属性值不互质,则可以匹配. 只考虑200以内的质数,一共有46个,可以新建3*46*46个点来表示一类属性值中有这两种质数的卡牌. 然后对于每张卡牌,枚举它的质因子,最多只有 ...
- Unity3D_(游戏)卡牌01_启动屏界面
卡牌2D游戏展示 (游戏代码放到 卡牌04_游戏界面 文章最后面~) 游戏项目已托管到github上(里面有个32bit可执行文件) 传送门 规则 开始游戏每张卡牌初始翻开展示 展示几秒后卡牌 ...
- 使用UIKit制作卡牌游戏(三)ios游戏篇
译者: Lao Jiang | 原文作者: Matthijs Hollemans写于2012/07/13 转自朋友Tommy 的翻译,自己只翻译了这第三篇教程. 原文地址: http://www.ra ...
随机推荐
- linux 系统目录权限
小结: 目录的读.写.执行权限 1. 可读r:表示具有浏览目录 下面文件及子目录的权限 ls dir 1)如果没有x权限,则不能进到目录,既无法执行cd dir 2)如果没有x权限,ls列表时可以看到 ...
- Quartz-第四篇 常规quartz的使用
1.目录结构 2.主要文件 1>引入的jar包,quartz-2.2.2解压后lib下所有的jar包 2>quartz.properties org.quartz.threadPool.t ...
- linux的管道 |和grep命令以及一些其他命令(diff,echo,cat,date,time,wc,which,whereis,gzip,zcat,unzip,sort)
linux提供管道符号“|”,作用是命令1的输出内容作为命令2的输入内容.通常与grep命令一起使用. 格式:命令1 |命令2 grep命令:全称为global regular expression ...
- python数据结构:pandas(1)
废话不说,直接上干货 一.数据结构 (1)Series:一维数组,与Numpy中的一维array类似.二者与Python基本的数据结构List也很相近.Series如今能保存不同种数据类型,字符串.b ...
- seajs与requirejs
1 seajs暴露的两个对象 二 define()定义 引用模块 三插件 css插件和requirejs插件 4 seajs使用和建议
- Hive导入导出数据的方法
Hive导入数据的方式 官网文档: LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (p ...
- [七月挑选]使用hexo建立主题,并发布到github
title: 使用hexo建立主题,并发布到github 根据hexo官网的概述和hexo官网的建站,搭建最开始的hexo博客. 1.环境预先安装好node.js和git 2.npm安装hexo: $ ...
- MongoDB的使用学习之(七)MongoDB的聚合查询(两种方式)附项目源码
先来张在路上…… 铛铛铛……项目源码下载地址:http://files.cnblogs.com/ontheroad_lee/MongoDBDemo.rar 此项目是用Maven创建的,没有使用Mave ...
- Vim 系列笔记一
Vim 系列笔记一 Vim 简介 什么是VIM ? Vim 是从 Vi 发展出来的一个编辑器,是 Vi 的升级版.而 vi 则是 Unix .类Unix(Linux)系统中自带的编辑器. Vim/Vi ...
- CSS的优先级理解
样式的优先级 多重样式(Multiple Styles):如果外部样式.内部样式和内联样式同时应用于同一个元素,就是使多重样式的情况. 一般情况下,优先级如下: (外部样式)External styl ...