Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P2065 [TJOI2011]卡片

P2065 [TJOI2011]卡片

显然是二分图的最大匹配。但是 $O(n ^ 2)$ 复杂度的建图是并不能接受的。考虑这道题的独特性质,两个数可以匹配当且仅当它们有约数,那么可以考虑将每个数分解质因数,每个数和整除它的质数连边,如图。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <cstdio>
#include <cstring>
#include <queue>
using std::min;
using std::queue;
const int A = 1e7 + 10, N = 3e3 + 10, M = 5e6 + 10, inf = 1e9;
bool vis[A];
int cnt, primes[A];
int m, n;
int st, ed, d[N], cur[N];
int idx, hd[N], nxt[M], edg[M], wt[M];
bool bfs()
{
memset(d, -1, sizeof d);
d[st] = 0;
cur[st] = hd[st];
queue<int> q;
q.push(st);
while (!q.empty())
{
int t = q.front();
q.pop();
for (int i = hd[t]; ~i; i = nxt[i])
if (d[edg[i]] == -1 && wt[i])
{
d[edg[i]] = d[t] + 1;
cur[edg[i]] = hd[edg[i]];
if (edg[i] == ed)
return true;
q.push(edg[i]);
}
}
return false;
}
int exploit(int x, int limit)
{
if (x == ed)
return limit;
int res = 0;
for (int i = cur[x]; ~i && res < limit; i = nxt[i])
{
cur[x] = i;
if (d[edg[i]] == d[x] + 1 && wt[i])
{
int t = exploit(edg[i], min(wt[i], limit - res));
if (!t)
d[edg[i]] = -1;
wt[i] -= t;
wt[i ^ 1] += t;
res += t;
}
}
return res;
}
int dinic()
{
int res = 0, flow;
while (bfs())
while (flow = exploit(st, inf))
res += flow;
return res;
}
void add(int a, int b, int c)
{
nxt[++idx] = hd[a];
hd[a] = idx;
edg[idx] = b;
wt[idx] = c;
}
void init(int x)
{
vis[1] = true;
for (int i = 2; i <= x; i++)
{
if (!vis[i])
primes[++cnt] = i;
for (int j = 1; j <= cnt && i <= x / primes[j]; j++)
{
vis[i * primes[j]] = true;
if (i % primes[j] == 0)
break;
}
}
}
int main()
{
init(A - 1);
int T;
scanf("%d", &T);
while (T--)
{
idx = -1;
memset(hd, -1, sizeof hd);
scanf("%d%d", &m, &n);
st = 0;
ed = m + n + 1;
for (int i = 1, a; i <= m; i++)
{
add(st, i, 1);
add(i, st, 0);
scanf("%d", &a);
for (int j = 1; j <= cnt; j++)
{
if (a % primes[j] == 0)
{
add(i, ed + j, 1);
add(ed + j, i, 0);
while (a != 1 && a % primes[j] == 0)
a /= primes[j];
}
if (a == 1)
break;
}
}
for (int i = m + 1, a; i <= m + n; i++)
{
add(i, ed, 1);
add(ed, i, 0);
scanf("%d", &a);
for (int j = 1; j <= cnt; j++)
{
if (a % primes[j] == 0)
{
add(ed + j, i, 1);
add(i, ed + j, 0);
while (a != 1 && a % primes[j] == 0)
a /= primes[j];
}
if (a == 1)
break;
}
}
printf("%d\n", dinic());
}
return 0;
}