目录:
题目:
分析:
分析题目,发现重点在于条件「一个数字只能参与一次配对」。
考虑求出
cnti
,表示
ai
分解质因数之后,每个质因数的指数之和。
那么
aia
和
aj
能配对的条件转化为:
ai
是
aj
的倍数,且
cnti=cntj+1
考虑一个二分图的模型。先按照
cnt
的奇偶性,把数字分为两个集合。
1、源点向所有
cnt
为奇数的点连一条容量为
bi
,费用为0的边。
2、所有
cnt
为偶数的点向汇点连一条容量为
bi
,费用为0的边。
3、对于一对
i,j
,如果
ai
和
aj
能配对并且
cnti
为奇数,则由
i
向
然后跑最大费用最大流。但是写法有一些变化:
由于跑最大费用最大流的过程中,每一次增广求出的最长路一定不会大于上一次增广求出的最长路,所以考虑贪心
每一次跑最长路后,沿着最长路,在价值总和不小于0的前提下尽可能增加流量。如果找不到增广路或者继续增广一定会使价值总和小于0 ,则已经传输的总流量就是答案。
AC后感想:
是真的难,还是扣学习大佬的算法才AC的。。。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef LL ll;
const int N = 210, M = 5e5 + 5; const ll INF = 1ll << 61;
int n, a[N], b[N], c[N], CNT[N], WWWYC = 1, nxt[M], WBD[N], st[M], go[M],
frm[M], S, T, len, que[M];
ll cap[M], DLJ[M], dis[N], sum, ans; bool vis[N];
void add_edge(int u, int v, ll w, ll x) {
nxt[++WWWYC] = WBD[u]; WBD[u] = WWWYC; st[WWWYC] = u;
go[WWWYC] = v; cap[WWWYC] = w; DLJ[WWWYC] = x;
nxt[++WWWYC] = WBD[v]; WBD[v] = WWWYC; st[WWWYC] = v;
go[WWWYC] = u; cap[WWWYC] = 0; DLJ[WWWYC] = -x;
}
int sigma(int n) {
int i, S = sqrt(n), tot = 0; for (i = 2; i <= S; i++)
while (n % i == 0) n /= i, tot++; if (n > 1) tot++; return tot;
}
bool spfa() {
int i; for (i = S; i <= T; i++) vis[i] = 0, dis[i] = -INF;
dis[que[len = 1] = S] = 0; for (i = 1; i <= len; i++) {
int u = que[i]; vis[u] = 0;
for (int e = WBD[u], v; e; e = nxt[e])
if (cap[e] && dis[u] + DLJ[e] > dis[v = go[e]]) {
dis[v] = dis[u] + DLJ[frm[v] = e];
if (!vis[v]) vis[que[++len] = v] = 1;
}
}
return dis[T] > -INF;
}
bool add() {
ll fl = INF, delta; for (int e = frm[T]; e; e = frm[st[e]])
fl = min(fl, cap[e]); delta = dis[T] * fl;
if (sum + delta >= 0) {
sum += delta; ans += fl;
for (int e = frm[T]; e; e = frm[st[e]])
cap[e] -= fl, cap[e ^ 1] += fl; return 1;
}
else return ans += sum / (-dis[T]), 0;
}
ll solve() {
while (spfa() && add()); return ans;
}
int main() {
int i, j; n = read(); for (i = 1; i <= n; i++) a[i] = read();
for (i = 1; i <= n; i++) b[i] = read();
for (i = 1; i <= n; i++) c[i] = read(); S = 1; T = n + 2;
for (i = 1; i <= n; i++) CNT[i] = sigma(a[i]);
for (i = 1; i <= n; i++) if (CNT[i] & 1) add_edge(S, i + 1, b[i], 0);
else add_edge(i + 1, T, b[i], 0);
for (i = 1; i <= n; i++) if (CNT[i] & 1) for (j = 1; j <= n; j++)
if ((CNT[i] + 1 == CNT[j] && a[j] % a[i] == 0) ||
(CNT[j] + 1 == CNT[i] && a[i] % a[j] == 0))
add_edge(i + 1, j + 1, INF, 1ll * c[i] * c[j]);
cout << solve() << endl; return 0;
}