[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 }
dinic
  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 }

 

posted @ 2019-11-10 17:06  hjj1871984569  阅读(284)  评论(0编辑  收藏  举报