BZOJ 4514: [Sdoi2016]数字配对

 

传送门

要求的即为,在费用不小于0的情况下的最大匹配数。

可转化为费用不大于0(负费用)的最大匹配。

在每次spfa得到的cost的时候就判断一下加上是否大于0了,如果大于0就可以直接加上答案并返回了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 3e4 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;

struct E { int v, ne, f;  ll c; } e[N];
int head[210], cnt, path[210], n, a[210], b[210];
ll dis[210], c[210];
int pm[32005], pn, si[210], sn, di[210], dn, ans;
bool vis[32005];

inline void add(int u, int v, int f, ll c) {
    e[cnt].v = v; e[cnt].f = f; e[cnt].c = -c; e[cnt].ne = head[u]; head[u] = cnt++;
    e[cnt].v = u; e[cnt].f = 0; e[cnt].c = c; e[cnt].ne = head[v]; head[v] = cnt++;
}

bool spfa(int s, int t) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(path, -1, sizeof(path));
    queue<int> que;
    dis[s] = 0;
    while (!que.empty()) que.pop();
    que.push(s);
    vis[s] = true;
    while (!que.empty()) {
        int u = que.front(); que.pop();
        vis[u] = false;
        for (int i = head[u]; ~i; i = e[i].ne) {
            int v = e[i].v, f = e[i].f;
            ll c = e[i].c;
            if (f && dis[v] > dis[u] + c) {
                dis[v] = dis[u] + c;
                path[v] = i;
                if (!vis[v]) {
                    vis[v] = true;
                    que.push(v);
                } 
            }
        }
    }
    return dis[t] != INF;                                               
}

void mcf(int s, int t) {
    ll cost = 0;
    while (spfa(s, t)) {
        int x = inf;
        for (int i = path[t]; ~i; i = path[e[i^1].v]) x = min(x, e[i].f);
        if (cost + dis[t] * x <= 0) {
            cost += dis[t] * x; 
            ans += x;
            for(int i = path[t]; ~i; i = path[e[i^1].v]) e[i].f -= x, e[i^1].f += x;
        } else {
            ans += (-cost) / dis[t];
            return;
        }
    }
}

void gp() {
    for (int i = 2; i <= 32000; i++) {
        if (!vis[i]) pm[++pn] = i;
        for (int j = 1; j <= pn && i * pm[j] <= 32000; j++) {
            vis[i * pm[j]] = true;
            if (i % pm[j] == 0) break;
        }
    } 
}

inline bool check(int x, int y) {
    if (!x || !y) return false;
    if (x < y) swap(x, y);
    if (x % y != 0) return false;
    x /= y;
    for (int i = 1; i <= pn; i++) {
        if (pm[i] >= x) break;
        if (x % pm[i] == 0) return false;
    }
    return true;
}

int main() {
    gp();
    memset(head, -1, sizeof(head));
    n = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= n; i++) b[i] = read();
    for (int i = 1; i <= n; i++) c[i] = read();
    for (int i = 1; i <= n; i++) {
        int temp = a[i], cnn = 0;
        for (int j = 1; j <= pn; j++) {
            while (temp % pm[j] == 0) {
                temp /= pm[j];
                cnn++;
            }
            if (temp == 1) break;
        }  
        if (cnn & 1) si[++sn] = i;
        else di[++dn] = i;
    }
    for (int i = 1; i <= sn; i++) {
        for (int j = 1; j <= dn; j++) {
            if (check(a[si[i]], a[di[j]])) {
                add(si[i], di[j], inf, c[si[i]] * c[di[j]]);
            }
        }
    }
    int s = n + 1, t = n + 2;
    for (int i = 1; i <= sn; i++) add(s, si[i], b[si[i]], 0);
    for (int i = 1; i <= dn; i++) add(di[i], t, b[di[i]], 0);
    mcf(s, t);
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2019-05-27 10:03  Mrzdtz220  阅读(126)  评论(0编辑  收藏  举报