• [SDOI2016]数字配对


    [SDOI2016]数字配对

    [题目链接]

    链接

    [思路要点]

    神仙构图(一点也不神仙)

    毫无疑问是一道网络流题

    很明显的费用流结构,每种数字单独建一个点,两个点代表的数字能够配对那么连一条边,边权就是 (c_ic_j)

    但是问题在于从源点和汇点出发的边不知道怎么连

    但是如果将目前连的图画出来,你会惊讶地发现这是一个二分图

    仔细思考你会发现,可以根据质因数的个数的奇偶性分类,由于两个数能配对当且仅当其质因数集合正好相差一个质数,那么质因数个数一定是一个奇数一个偶数

    于是我们得到一个图,现在要求的是这个图的总费用非负情况下的最大流

    考虑经典的最大费用流的算法,每次找到当前费用最大的一条路径并增广直到发现不了从 (s)(t) 的流量大于 (1) 的路径

    那么我们一个显然的想法是,如果当前寻到的路径的费用大于等于零,显然流满。然后记录下当前的费用,一直流到当前寻到的路径的费用与流量乘积使总费用小于零,此时就尽量流,流当前费用除以这条路径费用下取整这么多流量,然后你会发现这显然是对的

    [代码]

    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define int long long
    
    const int N = 1e3 + 31, M = 2e6 + 62, INF = 0x3f3f3f3f;
    struct edge {
        int to, next, w, c;
    } e[M << 1];
    int head[N], cnt = 1;
    void addedge(int x, int y, int z, int w) {
        e[++cnt] = (edge){y, head[x], z, w}, head[x] = cnt;
        e[++cnt] = (edge){x, head[y], 0, -w}, head[y] = cnt;
    }
    int ek(int s, int t) {
        static int dis[N], vis[N], pre[N], flow[N];
        std::queue<int> q;
        int ret = 0, m = 0;
        while ("每日一膜:G♂LX") {
            memset(dis, 0xc0, sizeof dis);
            flow[s] = INF, dis[s] = 0;
            for (q.push(s); !q.empty();) {
                int x = q.front();
                vis[x] = 0;
                q.pop();
                for (int i = head[x]; i; i = e[i].next) {
                    int nx = e[i].to;
                    if (e[i].w && dis[nx] < dis[x] + e[i].c) {
                        dis[nx] = dis[x] + e[i].c;
                        flow[nx] = std::min(flow[x], e[i].w);
                        pre[nx] = i;
                        if (!vis[nx]) vis[nx] = 1, q.push(nx);
                    }
                }
            }
            if (dis[t] == 0xc0c0c0c0) return ret;
            if (m + flow[t] * dis[t] < 0) return ret - m / dis[t];
            for (int i = pre[t]; i; i = pre[e[i ^ 1].to]) {
                e[i].w -= flow[t];
                e[i ^ 1].w += flow[t];
            }
            ret += flow[t];
            m += flow[t] * dis[t];
        }
    }
    
    int $(int x) {
        int ret = 0;
        for (int i = 2; i <= sqrt(x); i++)
            for (; x % i == 0; ret++) x /= i;
        return ret + (x > 1);
    }
    int n, a[N], b[N], c[N], f[N];
    signed main() {
        scanf("%lld", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", a + i);
            f[i] = $(a[i]);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%lld", b + i);
            if (f[i] & 1)
                addedge(1, i + 2, b[i], 0);
            else
                addedge(i + 2, 2, b[i], 0);
        }
        for (int i = 1; i <= n; i++) scanf("%lld", c + i);
        for (int i = 1; i <= n; i++) {
            if (f[i] & 1)
                for (int j = 1; j <= n; j++) {
                    if ((f[i] == f[j] + 1 && a[i] % a[j] == 0) || (f[j] == f[i] + 1 && a[j] % a[i] == 0))
                        addedge(i + 2, j + 2, INF, c[i] * c[j]);
                }
        }
        printf("%lld", ek(1, 2));
    }
    
  • 相关阅读:
    888. Uncommon Words from Two Sentences
    344. Reverse String
    151. Reverse Words in a String
    557. Reverse Words in a String III
    811. Subdomain Visit Count
    上海市公积金、养老保险、医疗保险转出事宜
    476. Number Complement
    方法重载的条件
    简单工厂模式
    单例模式
  • 原文地址:https://www.cnblogs.com/wawawa8/p/11113563.html
Copyright © 2020-2023  润新知