题目:卡牌配对

传送门: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. [JS] 鼠标点击文本框清空默认值,离开文本框恢复默认值

    在使用文本框的时候,若设定了初始值,选择文本框进行输入的时候要将本来的内容进行删除,会显得非常麻烦 可以在文本框属性定义触发onfocus和onblur两个事件时对应的js功能 下面以asp.net代 ...

  2. librdkafka 安装

    1,Git clone git clone https://github.com/edenhill/librdkafka.git 2,cd librdkafka/ 3,./configure 4,ma ...

  3. [19/06/07-星期五] CSS基础_布局&定位&背景样式

    一.固定布局(不适应设备的浏览器的变化) <!DOCTYPE html> <html> <head> <meta charset="UTF-8&qu ...

  4. 使用批处理命令注册运行mysql数据库,无需注册mysql服务,可以在任意电脑登录使用

    使用批处理命令初始化和开启mysql服务,移植数据库之后可以直接运行访问,对于学习数据库的人来说特别的方便哦. 我们可以从mysql官网下载官方社区版本的mysql: 这里使用之前下载的8.0.15来 ...

  5. CF573E Bear and Bowling

    题目 我们设\(f_{i,j}\)表示前\(i\)个数中选\(j\)个的最大值. 那么显然有\(f_{i,j}=max(f_{i-1,j},f_{i-1,j-1}+j*a_i)\). 这个东西我们首先 ...

  6. mui前端框架下拉刷新分页加载数据

    前台 mui.init(); (function($) { //阻尼系数 var deceleration = mui.os.ios?0.003:0.0009; $('.mui-scroll-wrap ...

  7. RabbitMq学习3-工作队列(Work queues)

    工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源.时间的操作.当我们把任务(Task)当作消息发送到队列中,一个运行在后台的工作者(worker)进程就会取出任务然后 ...

  8. object in javascript

    枚举对象属性 for....in 列举obj的可枚举属性,包括自身和原型链上的 object.keys() 只列举对象本身的可枚举属性 创建对象的几种方式 对象字面量 const pre='test' ...

  9. js面向过程-拖拽

    1.步骤分析: 1.1 获取id 1.2 当鼠标点击时执行的js 1.3当鼠标移动时执行的js 1.4当鼠标放开时执行的js 2.代码实现 <!DOCTYPE html> <html ...

  10. 简单的物流项目实战,WPF的MVVM设计模式(五)

    开始界面 <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowD ...