题目链接:https://www.nowcoder.com/acm/contest/130/D
解题思路:网络流,预处理出所有的(i,j)对和(p,q)对,源点S和(i,j)连容量为1的边,(p,q)和汇点T连容量为1的边,如果(i,j)和(p,q)可以同时选择则连容量为1的边。跑一遍最大流即可得出答案。
边数可能很大,注意到(i,j)和(p,q)可以连边的条件是不互质,可以将质因子作为中间点,(i,j)如果有质因子v,则(i,j)向v连一条容量为INF的边,(p,q)同理。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;
int n, cnt;
int a[409], b[409];
int prime[400009], tot, vis[400009];
map<int, int>in, out;
struct node
{
int u, v, next, cap;
} edge[4009 * 10 * 409];
int s[160009], d[160009], visit[160009];
int gcd(int x, int y) {
return x ? gcd(y % x, x) : y;
}
void init() {
cnt = 0;
memset(s, -1, sizeof s);
tot = 0;
vis[0] = vis[1] = 1;
for (int i = 2; 1LL * i * i < 1e9; i++) {
if (!vis[i]) {
prime[tot++] = i;
for (int j = 2 * i; 1LL * j * j <= 1e9; j += i) vis[j] = 1;
}
}
}
void add(int u, int v, int c)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].cap = c;
edge[cnt].next = s[u];
s[u] = cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].cap = 0;
edge[cnt].next = s[v];
s[v] = cnt++;
}
bool BFS(int ss, int ee)
{
memset(d, 0, sizeof d);
d[ss] = 1;
queue<int>q;
q.push(ss);
while (!q.empty())
{
int pre = q.front();
q.pop();
for (int i = s[pre]; ~i; i = edge[i].next)
{
int v = edge[i].v;
if (edge[i].cap > 0 && !d[v])
{
d[v] = d[pre] + 1;
q.push(v);
}
}
}
return d[ee];
}
int DFS(int x, int exp, int ee)
{
if (x == ee || !exp) return exp;
int temp, flow = 0;
for (int i = s[x]; ~i; i = edge[i].next)
{
int v = edge[i].v;
if (d[v] == d[x] + 1 && (temp = (DFS(v, min(exp, edge[i].cap), ee))) > 0)
{
edge[i].cap -= temp;
edge[i ^ 1].cap += temp;
flow += temp;
exp -= temp;
if (!exp) break;
}
}
if (!flow) d[x] = 0;
return flow;
}
int Dinic_flow(int ss, int ee)
{
int ans = 0;
while (BFS(ss, ee))
{
ans += DFS(ss, INF, ee);
}
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
init();
in.clear();
out.clear();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int tmp = gcd(a[i], b[j]);
if (tmp == 1) continue;
if (a[i] < b[j]) in[tmp]++;
if (a[i] > b[j]) out[tmp]++;
}
}
int insum = in.size();
int outsum = out.size();
int p = 1;
for (map<int, int>::iterator it = in.begin(); it != in.end(); it++, p++) {
int tmp = it->first;
add(0, p + 1, it->second);
for (int k = 0; k < tot && prime[k] <= tmp; k++) {
if (tmp % prime[k] == 0) {
add(p + 1, insum + outsum + k + 2, INF);
while (tmp % prime[k] == 0) tmp /= prime[k];
}
}
if (tmp != 1) {
prime[tot++] = tmp;
add(p + 1, insum + outsum + tot + 1, INF);
}
}
p = 1;
for (map<int, int>::iterator it = out.begin(); it != out.end(); it++, p++) {
int tmp = it->first;
add(p + 1 + insum, 1, it->second);
for (int k = 0; k < tot && prime[k] <= tmp; k++) {
if (tmp % prime[k] == 0) {
add(insum + outsum + 2 + k, p + 1 + insum, INF);
while (tmp % prime[k] == 0) tmp /= prime[k];
}
}
if (tmp != 1) {
prime[tot++] = tmp;
add(insum + outsum + 1 + tot, p + 1 + insum, INF);
}
}
printf("%d\n", Dinic_flow(0, 1));
}
return 0;
}