BZOJ4514 [Sdoi2016]数字配对 【费用流】

题目

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。

若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,

那么这两个数字可以配对,并获得 ci×cj 的价值。

一个数字只能参与一次配对,可以不参与配对。

在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

输入格式

第一行一个整数 n。

第二行 n 个整数 a1、a2、……、an。

第三行 n 个整数 b1、b2、……、bn。

第四行 n 个整数 c1、c2、……、cn。

输出格式

一行一个数,最多进行多少次配对

输入样例

3

2 4 8

2 200 7

-1 -2 1

输出样例

4

提示

n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5

题解

如果\(a_i\)和\(a_j\)之间相差一个质数,那么它们一定是互为倍数关系且质因子分解后指数和只相差\(1\)

容易发现,相互匹配的两个数的指数和一定是一奇一偶

所以这是一个二分图匹配问题

用最大费用最大流实现匹配即可

但由于费用和非负,我们在最大流统计流量时,如果当前答案加上本次新增费用小于0,就刚好取到不小于0的部分

由于先取大的一定比取小的好,所以这样的贪心策略正确

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 205,maxm = 200005;
const LL INF = 1000000000000000000ll;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int n,ans;
LL a[maxn],b[maxn],c[maxn],cnt[maxn],sum;
LL d[maxn],minf[maxn];
int p[maxn];
int h[maxn],ne = 2,S,T;
struct EDGE{int to,nxt; LL f,w;}ed[maxm];
inline void build(int u,int v,LL f,LL w){
    ed[ne] = (EDGE){v,h[u],f,w}; h[u] = ne++;
    ed[ne] = (EDGE){u,h[v],0,-w}; h[v] = ne++;
}
void Sp(int u){
    int x = a[u];
    for (int i = 2; i <= x; i++)
        if (x % i == 0){
            while (x % i == 0) cnt[u]++,x /= i;
        }
    if (x - 1) cnt[u]++;
}
queue<int> q;
int inq[maxn];
void mincost(){
    while (true){
        for (int i = S; i <= T; i++) inq[i] = p[i] = 0,d[i] = INF;
        q.push(S); d[S] = 0; minf[S] = INF;
        int u;
        while (!q.empty()){
            u = q.front(); q.pop();
            inq[u] = false;
            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                d[to] = d[u] + ed[k].w; minf[to] = min(minf[u],ed[k].f);
                p[to] = k;
                if (!inq[to]) q.push(to),inq[to] = true;
            }
        }
        if (d[T] == INF) return;
        if (d[T] <= 0){
            sum += -d[T] * minf[T];
            ans += minf[T];
            u = T;
            while (u != S){
                ed[p[u]].f -= minf[T];
                ed[p[u] ^ 1].f += minf[T];
                u = ed[p[u] ^ 1].to;
            }
        }
        else {
            int l = min(minf[T],sum / d[T]);
            if (!l) return;
            sum += -d[T] * l;
            ans += l;
            u = T;
            while (u != S){
                ed[p[u]].f -= l;
                ed[p[u] ^ 1].f += l;
                u = ed[p[u] ^ 1].to;
            }
        }
    }
}
int main(){
    n = read(); S = 0; T = n + 1;
    REP(i,n) a[i] = read(),Sp(i);
    REP(i,n){
        b[i] = read();
        if (cnt[i] & 1) build(S,i,b[i],0);
        else build(i,T,b[i],0);
    }
    REP(i,n) c[i] = read();
    REP(i,n) REP(j,n){
        if ((cnt[i] & 1) && !(cnt[j] & 1) && ((a[i] % a[j] == 0 && cnt[i] == cnt[j] + 1) || (a[j] % a[i] == 0 && cnt[j] == cnt[i] + 1))){
            build(i,j,INF,-(c[i] * c[j]));
        }
    }
    mincost();
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8868553.html

时间: 2024-10-07 04:18:16

BZOJ4514 [Sdoi2016]数字配对 【费用流】的相关文章

BZOJ4514: [Sdoi2016]数字配对(费用流)

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2197  Solved: 859[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最

Luogu P4068 [SDOI2016]数字配对(费用流)

Luogu P4068 [SDOI2016]数字配对(费用流) 根据质因子个数奇偶性划分肯定会形成一张二分图. 把所有的\(a\)分解质因数,记录其质因子个数. \(a_i \% a_j == 0\)且\(a_i\)的质因子比\(a_j\)质因子个数多1的时候,我们连边. 解决这个题目的关键是求出费用\(>0\)的时候的最大的流量. 我们要跑最大费用最大流,(具体实现是把边权取反) 这样在每一次的增广过程中,我们都可以保证费用最大且满足流最多. 但是写法有异议,待填坑. 原文地址:https:/

【BZOJ4514】[Sdoi2016]数字配对 费用流

[BZOJ4514][Sdoi2016]数字配对 Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. Input 第一行一个整数 n. 第二行 n 个整数 a1.a2.…….an. 第三行 n 个整数 b1.b2.

【BZOJ 4514】[Sdoi2016]数字配对 费用流

利用spfa流的性质,我直接拆两半,正解分奇偶(妙),而且判断是否整除且质数我用的是暴力根号,整洁判断质数个数差一(其他非spfa流怎么做?) #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; const int N=205; const int P=N; const int E=N*N/2; const int Inf=0x3f3f3f3f; const LL

[Bzoj4514][Sdoi2016]数字配对(费用流)

4514: [Sdoi2016]数字配对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2204  Solved: 865[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对.

【BZOJ4514】【SDOI2016】数字配对 [费用流]

数字配对 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. Input 第一行一

bzoj4514 [Sdoi2016]数字配对(网络流)

Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. Input 第一行一个整数 n. 第二行 n 个整数 a1.a2.--.an. 第三行 n 个整数 b1.b2.--.bn. 第四行 n 个整数 c1.c2.--

[bzoj4514] [Sdoi2016]数字配对

费用流.. 先把a数组里的数全部质因数分解,判断ai/aj是否为质数,就看质因数互相抵消后是不是只剩一个质因数. 满足条件的数就连边 接下来我想拆点建二分图..然而由题解可得,连边的两个数 的质因数个数 的奇偶性肯定不同.. 就相当于自带黑白染色...所以奇数个连汇,偶数个连源就行了= = 因为有价值总和不小于0的限制.所以找到一条价值为负的增广路后,不一定能流满. 但是因为求费用流的原始对偶算法自带限制流量的功能..所以其实改一个数就能照常多路增广了= = 1 #include<cstdio>

[SDOI2016][bzoj4514] 数字配对 [费用流]

题面 传送门 思路 一个数字能且只能匹配一次 这引导我们思考:一次代表什么?代表用到一定上限(b数组)就不能再用,同时每用一次会产生价值(c数组) 上限?价值?网络流! 把一次匹配设为一点流量,那产生的价值不就是费用了吗? 我们考虑把一种数字抽象成一个点,可以匹配的数字之间连边,费用为c[i]*c[j],流量上限为..... 等等,流量上限怎么设? 而且还有一个问题:这里的匹配是双向的,虽然可以$O\left(n^2\right)$求出所有匹配对,但是网络流要求是单向边啊! 别急,我们先来分析一