bzoj4514 数字配对

思路

首先想到费用流。
对于每个点拆点。然后考虑我们怎样才能保证每个点只被用一次。
如果\(i\)与\(j\)满足条件。那么就从\(i\)向\(j\)连一条边并且从\(j\)向\(i\)连一条边。这样每次增广的时候我们都可以看作某一条边被增广了两次。显然从\(i\)到\(j\)和从\(j\)到\(i\)的边是等价的。也就是说,如果当前增广这两个点之间的边更优秀,那么在增广完成从\(i\)到\(j\)和从\(j\)到\(i\)这两条边流量变为\(0\)之前不回去增广其他的边。
比较难解释,仔细想一下可以发现是对的。这样最后我们找出的流量实际上是答案的两倍。除二即可。
然后还要考虑题目中对于价值的限制。我们把价值当作费用,每次增广费用最大的路径。直到如果再增广费用变为负数为止。

代码

/*
* @Author: wxyww
* @Date:   2019-02-17 14:52:25
* @Last Modified time: 2019-02-17 19:36:45
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 410,M = 1000000 + 100,INF = 1e9;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
struct node {
    int v,nxt,w;
    ll cost;
}e[M];
int head[N],ejs = 1;
void add(int u,int v,int w,ll c) {
    e[++ejs].v = v;e[ejs].w = w;e[ejs].cost = c;e[ejs].nxt = head[u];head[u] = ejs;
    e[++ejs].v = u;e[ejs].w = 0;e[ejs].cost = -c;e[ejs].nxt = head[v];head[v] = ejs;
}
int a[N],vis[N],fa[N],b[N];
ll dis[N],c[N];
queue<int>q;
int S,T;
bool pd(int x,int y) {
    if(x < y) swap(x,y);
    if(!y || x == y) return false;
    if(x % y) return false;
    int k = x/y;
    for(int i = 2;i * i <= k;++i)
        if(k % i == 0) return false;
    return true;
}
bool spfa() {
    while(!q.empty()) q.pop();
    memset(dis,-0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(fa,0,sizeof(fa));
    q.push(S);dis[S] = 0;
    while(!q.empty()) {
        int u = q.front();q.pop();vis[u] = 0;
        for(int i = head[u];i;i = e[i].nxt) {
            int v = e[i].v;
            if(dis[v] < dis[u] + e[i].cost && e[i].w) {
                dis[v] = dis[u] + e[i].cost;
                fa[v] = i;
                if(!vis[v]) q.push(v),vis[v] = 1;
            }
        }
    }
    return fa[T];
}
ll dinic() {
    ll COST = 0,FLOW = 0;
    while(spfa()) {
        int mn = INF;
        for(int i = fa[T];i;i = fa[e[i ^ 1].v]) mn = min(mn,e[i].w);
        for(int i = fa[T];i;i = fa[e[i ^ 1].v]) e[i].w -= mn,e[i ^ 1].w += mn;
        if(COST + dis[T] * mn < 0) {
            FLOW += COST / -dis[T];
            return FLOW;
        }
        COST += dis[T] * mn;
        FLOW += mn;
    }
    return FLOW;
}
int main() {
    int 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();
    S = n * 2 + 1,T = S + 1;
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            if(i != j && pd(a[j],a[i]))
                add(i,j + n,INF,c[i] * c[j]);
    int tot = ejs;
    for(int i = 1;i <= n;++i) add(S,i,b[i],0);
    for(int i = 1;i <= n;++i) add(i + n,T,b[i],0);
    cout<<(dinic() >> 1);
    return 0;
}

原文地址:https://www.cnblogs.com/wxyww/p/bzoj4514.html

时间: 2024-11-05 14:56:41

bzoj4514 数字配对的相关文章

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

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

【bzoj4514】: [Sdoi2016]数字配对 图论-费用流

[bzoj4514]: [Sdoi2016]数字配对 好像正常的做法是建二分图? 我的是拆点然后 S->i cap=b[i] cost=0 i'->T cap=b[i] cost=0 然后能匹配的两点i,j 连 i->j' cap=inf cost=c[i]*c[j] 跑最大费用流,直到 cost<0 或 全部增广完 最后flow/2就是答案 1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #i

【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.

bzoj4514【SDOI2016】数字配对

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

【BZOJ-4514】数字配对 最大费用最大流 + 质因数分解 + 二分图 + 贪心 + 线性筛

4514: [Sdoi2016]数字配对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 726  Solved: 309[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]数字配对(费用流)

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 的价值. 一个数字只能参与一次配对,可以不参与配对.

图论(费用流):BZOJ 4514 [Sdoi2016]数字配对

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

4514: [Sdoi2016]数字配对

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