[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 ...
随机推荐
- python控制流-名词解释
一.控制流的元素 控制流语句的开始部分通常是“条件”,接下来是一个代码块,称为“子句”. 二.控制流的条件 条件为了判断下一步如何进行,从而求布尔值的表达式.几乎所有的控制流语句都使用条件. 三.代码 ...
- unsigned char bcd串乱码问题解决
unsigned char bcd[13]; ...... string bcdstr; for(int i=0;i < 12;i++) { bcdstr=FormatString(" ...
- gcc 消除未使用变量的警告
我们写代码的时候经常需要遇到一些情况,参数暂时没用到.但是这个参数必须存在. 例如linux下线程实体函数void *thread_xx(void *arg)如果不处理,gcc编译时就会报" ...
- glide使用总结
1 glide是什么 glide是一个图片加载和缓存库. 2 glide的使用 第一,添加依赖 implementation 'com.github.bumptech.glide:glide:4.5. ...
- 关于Pulsar与Kafka
在本系列的Pulsar和Kafka比较文章中,我将引导您完成我认为重要的几个领域,并且对于人们选择强大,高可用性,高性能的流式消息传递平台至关重要.消息传递模型(Messaging model)是用户 ...
- IDEA在resources下创建多级目录
在resource下,创建多级目录,应在每个目录之间用"/"隔开,这样就不需要再手动一层层目录的分别添加了!
- HeidiSQL
相关链接 https://www.heidisql.com/ - 官网 https://github.com/HeidiSQL/HeidiSQL - 源码 参考 ...
- luogu P1397 [NOI2013]矩阵游戏
传送门 题目中那两个递推式显然可以写成矩乘的形式,然后十进制快速幂即可.这里不再赘述 只有两个递推式,我们可以考虑一波推式子,首先第一行的元素应该分别是\(1,a+b,a^2+ab+b,a^3+a^2 ...
- 自定义ajax函数(仿照jQuery)
AJAX介绍 AJAX = 异步 JavaScript 和 XML. 全称:Asynchronous Javascript And XML: AJAX 是一种用于创建快速动态网页的技术. 通过在后台与 ...
- listalias - 列出用户和系统别名
总揽 listalias [ -s | -u ] [ 正则表达式] 描述 Listalias 按用户及系统别名每个输出一行.每行具有下列格式: <别名> <地址> (<注 ...