题目:卡牌配对

传送门: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. Rocketmq-概念

    一.Rcoketmq Rocketmq是一个消息中间件,简单来说就是传递消息用的. 二.Rocketmq构成 1.Rocketmq组件不是单个的软件,它是由四个组件构成的: (1)Producer 消 ...

  2. 【6.10校内test】T2 医院设置

    医院设置[题目链接] 感觉我超废 我是一个连floyd都不会写了的灵魂OI选手qwq(考场上写了半天spfa然后写炸了(微笑)) floyd的暴力: 1.先建树:用邻接矩阵存.存之前记得先初始化为IN ...

  3. 搜索专题:HDU1241 Oil Deposits

    Oil Deposits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tot ...

  4. MySQL: InnoDB的并发控制,锁,事务模型

    一.并发控制 为啥要进行并发控制? 并发的任务对同一个临界资源进行操作,如果不采取措施,可能导致不一致,故必须进行并发控制(Concurrency Control). 技术上,通常如何进行并发控制? ...

  5. Nginx 入门了解

    Nginx的产生 没有听过Nginx?那么一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一资源描述符(Unifor ...

  6. 剑指offer-回溯法-机器人的运动范围-python

    题目描述 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子. 例如,当k为18时,机器人能 ...

  7. Css网页样式设计

    第一章 概述 一.CSS简介1.CSS是Cascading Style Sheets(层叠样式表单)的简称.通常所称的CSS是指CSS1,即层叠样式表单1级. 2.编辑CSS文档:与编辑HTML的方法 ...

  8. oracle比较两个查询结果的差异

    可以使用minus select * from A minus select * from B; select * from B minus select * from A;

  9. PAT Advanced 1007 Maximum Subsequence Sum (25 分)

    Given a sequence of K integers { N​1​​, N​2​​, ..., N​K​​ }. A continuous subsequence is defined to ...

  10. 2014百度之星初赛第二场hdu 4831 Scenic Popularity

    Scenic Popularity Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...