[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$
考虑到大量属性值点对可能不会出现,于是采用类似记忆化的做法加速
每个属性点可以先预处理出他的质因数,这样可以再优化一下
这道题节点数和边数非常的多,然后“请大胆使用渐进复杂度较高的做法”是通过本题的关键 = =
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 80005; 4 const int MAXM = 4000005; 5 const int INF = 1e9 + 7; 6 typedef int LL; 7 int pn; 8 vector <int> prime, markp[201], mark[201][201]; 9 void init() { 10 for(int i = 2; i <= 200; i++) { 11 int fg = 1; 12 for(int j = 2; j < i; j++) 13 if(i % j == 0) fg = 0; 14 if(fg) prime.push_back(i); 15 } 16 pn = prime.size(); 17 for(int i = 2; i <= 200; i++) 18 for(int j = 0; j < (int)prime.size(); j++) 19 if(i % prime[j] == 0) markp[i].push_back(j); 20 } 21 void getMark(int a, int b) { 22 for(auto i : markp[a]) 23 for(auto j : markp[b]) 24 if(a % prime[i] == 0 && b % prime[j] == 0) 25 mark[a][b].push_back(i * pn + j); 26 } 27 namespace NWF { 28 struct Edge { 29 int to, nxt;LL f; 30 } e[MAXM << 1]; 31 int S, T, tot; 32 int ecnt, head[MAXN], cur[MAXN], dis[MAXN]; 33 queue<int> q; 34 void init(int _S, int _T, int _tot){ 35 ecnt = 1; S = _S; T = _T; tot = _tot; 36 memset(head, 0, (tot + 1) * sizeof(int)); 37 } 38 void addEdge(int u, int v, LL f) { 39 e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt; 40 e[++ecnt] = (Edge) {u, head[v], 0}; head[v] = ecnt; 41 } 42 bool bfs() { 43 memset(dis, 0, (tot + 1) * sizeof(int)); 44 q.push(S); dis[S] = 1; 45 while (!q.empty()) { 46 int u = q.front(), v; q.pop(); 47 for (int i = cur[u] = head[u]; i ; i = e[i].nxt) { 48 if (e[i].f && !dis[v = e[i].to]) { 49 q.push(v); 50 dis[v] = dis[u] + 1; 51 } 52 } 53 } 54 return dis[T]; 55 } 56 LL dfs(int u, LL maxf) { 57 if (u == T) return maxf; 58 LL sumf = maxf; 59 for (int &i = cur[u]; i; i = e[i].nxt) { 60 if (e[i].f && dis[e[i].to] > dis[u]) { 61 LL tmpf = dfs(e[i].to, min(sumf, e[i].f)); 62 e[i].f -= tmpf; e[i ^ 1].f += tmpf; 63 sumf -= tmpf; 64 if (!sumf) return maxf; 65 } 66 } 67 return maxf - sumf; 68 } 69 LL dinic() { 70 LL ret = 0; 71 while (bfs()) ret += dfs(S, INF); 72 return ret; 73 } 74 } 75 int main() { 76 freopen("4205_19.in","r",stdin); 77 init(); 78 int n1, n2; 79 scanf("%d%d",&n1,&n2); 80 int n = n1 + n2; 81 NWF::init(pn *pn * 3 + n, pn * pn * 3 + n + 1, pn * pn * 3 + n + 2); 82 for(int k = 0,a,b,c; k < n1; k++) { 83 scanf("%d%d%d",&a,&b,&c); 84 int id = pn * pn * 3 + k; 85 NWF::addEdge(NWF::S, id, 1); 86 if(mark[a][b].size() == 0 && a != 1 && b != 1) getMark(a, b); 87 if(mark[a][c].size() == 0 && a != 1 && c != 1) getMark(a, c); 88 if(mark[b][c].size() == 0 && b != 1 && c != 1) getMark(b, c); 89 for(auto it : mark[a][b]) NWF::addEdge(id, it, 1); 90 for(auto it : mark[a][c]) NWF::addEdge(id, pn*pn+it, 1); 91 for(auto it : mark[b][c]) NWF::addEdge(id, 2*pn*pn+it, 1); 92 } 93 for(int k = 0,a,b,c; k < n2; k++) { 94 scanf("%d%d%d",&a,&b,&c); 95 int id = pn * pn * 3 + n1 + k; 96 NWF::addEdge(id, NWF::T, 1); 97 if(mark[a][b].size() == 0 && a != 1 && b != 1) getMark(a, b); 98 if(mark[a][c].size() == 0 && a != 1 && c != 1) getMark(a, c); 99 if(mark[b][c].size() == 0 && b != 1 && c != 1) getMark(b, c); 100 for(auto it : mark[a][b]) NWF::addEdge(it, id, 1); 101 for(auto it : mark[a][c]) NWF::addEdge(pn*pn+it, id, 1); 102 for(auto it : mark[b][c]) NWF::addEdge(2*pn*pn+it, id, 1); 103 } 104 int ans = NWF::dinic(); 105 printf("%d\n",ans); 106 return 0; 107 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 80005; 4 const int MAXM = 4000005; 5 const int INF = 1e9 + 7; 6 typedef int LL; 7 int pn; 8 vector <int> prime, markp[201], mark[201][201]; 9 void init() { 10 for(int i = 2; i <= 200; i++) { 11 int fg = 1; 12 for(int j = 2; j < i; j++) 13 if(i % j == 0) fg = 0; 14 if(fg) prime.push_back(i); 15 } 16 pn = prime.size(); 17 for(int i = 2; i <= 200; i++) 18 for(int j = 0; j < (int)prime.size(); j++) 19 if(i % prime[j] == 0) markp[i].push_back(j); 20 } 21 void getMark(int a, int b) { 22 for(auto i : markp[a]) 23 for(auto j : markp[b]) 24 if(a % prime[i] == 0 && b % prime[j] == 0) 25 mark[a][b].push_back(i * pn + j); 26 } 27 namespace NWF { 28 struct Edge{ 29 int to, nxt;LL f; 30 }e[MAXM << 1]; 31 int S, T, tot; 32 int ecnt, head[MAXN], cur[MAXN], pre[MAXN], num[MAXN], dis[MAXN]; 33 queue<int> q; 34 void init(int _S, int _T, int _tot){ 35 ecnt = 1; S = _S; T = _T; tot = _tot; 36 memset(num, 0, (tot + 1) * sizeof(int)); 37 memset(head, 0, (tot + 1) * sizeof(int)); 38 } 39 inline void addEdge(int u, int v, LL f) { 40 e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt; 41 e[++ecnt] = (Edge) {u, head[v], 0}; head[v] = ecnt; 42 } 43 void bfs() { 44 memset(dis, 0, (tot + 1) * sizeof(int)); 45 q.push(T); 46 dis[T] = 1; 47 while(!q.empty()) { 48 int u = q.front(), v; q.pop(); 49 num[dis[u]]++; 50 for(int i = cur[u] = head[u]; i; i = e[i].nxt) { 51 if(!dis[v = e[i].to]) { 52 dis[v] = dis[u] + 1; 53 q.push(v); 54 } 55 } 56 } 57 } 58 LL augment() { 59 LL flow = INF; 60 for(int i = S; i != T; i = e[cur[i]].to) 61 flow = min(flow, e[cur[i]].f); 62 for(int i = S; i != T; i = e[cur[i]].to) { 63 e[cur[i]].f -= flow; 64 e[cur[i] ^ 1].f += flow; 65 } 66 return flow; 67 } 68 LL isap() { 69 bfs(); 70 int u = S, v; 71 LL flow = 0; 72 while(dis[S] <= tot) { 73 if(u == T) { 74 flow += augment(); 75 u = S; 76 } 77 bool fg = 0; 78 for(int i = cur[u]; i; i = e[i].nxt) { 79 if(e[i].f && dis[u] > dis[v = e[i].to]) { 80 pre[v] = u; 81 cur[u] = i; 82 u = v; 83 fg = 1; 84 break; 85 } 86 } 87 if(fg) continue; 88 if(!--num[dis[u]]) break; 89 int maxDis = tot; 90 for(int i = head[u]; i; i = e[i].nxt) { 91 if(e[i].f && maxDis > dis[v = e[i].to]) { 92 maxDis = dis[v]; 93 cur[u] = i; 94 } 95 } 96 num[dis[u] = maxDis + 1]++; 97 if(u != S) u = pre[u]; 98 } 99 return flow; 100 } 101 } 102 int main() { 103 init(); 104 int n1, n2; 105 scanf("%d%d",&n1,&n2); 106 int n = n1 + n2; 107 NWF::init(pn *pn * 3 + n, pn * pn * 3 + n + 1, pn * pn * 3 + n + 2); 108 for(int k = 0,a,b,c; k < n1; k++) { 109 scanf("%d%d%d",&a,&b,&c); 110 int id = pn * pn * 3 + k; 111 NWF::addEdge(NWF::S, id, 1); 112 if(mark[a][b].size() == 0 && a != 1 && b != 1) getMark(a, b); 113 if(mark[a][c].size() == 0 && a != 1 && c != 1) getMark(a, c); 114 if(mark[b][c].size() == 0 && b != 1 && c != 1) getMark(b, c); 115 for(auto it : mark[a][b]) NWF::addEdge(id, it, 1); 116 for(auto it : mark[a][c]) NWF::addEdge(id, pn*pn+it, 1); 117 for(auto it : mark[b][c]) NWF::addEdge(id, 2*pn*pn+it, 1); 118 } 119 for(int k = 0,a,b,c; k < n2; k++) { 120 scanf("%d%d%d",&a,&b,&c); 121 int id = pn * pn * 3 + n1 + k; 122 NWF::addEdge(id, NWF::T, 1); 123 if(mark[a][b].size() == 0 && a != 1 && b != 1) getMark(a, b); 124 if(mark[a][c].size() == 0 && a != 1 && c != 1) getMark(a, c); 125 if(mark[b][c].size() == 0 && b != 1 && c != 1) getMark(b, c); 126 for(auto it : mark[a][b]) NWF::addEdge(it, id, 1); 127 for(auto it : mark[a][c]) NWF::addEdge(pn*pn+it, id, 1); 128 for(auto it : mark[b][c]) NWF::addEdge(2*pn*pn+it, id, 1); 129 } 130 int ans = NWF::isap(); 131 printf("%d\n",ans); 132 return 0; 133 }