BZOJ 4514: [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、……、cn。


Output

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


Sample Input

3
2 4 8
2 200 7
-1 -2 1


Sample Output

4


HINT

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


Solution

题目要求两两匹配,考虑二分图

由于有费用限制,考虑费用流

构图可以很无脑,左右两边各一排点,容量为b[i],满足条件的两个点连边,费用为c[i]*c[j],然后跑费用流,乱搞出最大匹配数,除以2就是答案。

我们也可以从条件的性质入手,我们发现将所有数唯一分解后,如果A能连向B的必要条件是A和B的质因子个数的奇偶性不同。假如奇偶性相同,A是B的倍数,那么必然A就比B多偶数个质因子,而题目只让多一个。

所以质因子数为奇数的放左边,偶数的放右边,按题意连边跑费用流。如果当前跑完一次SPFA后总费用变成负数了,代表这次的流量不能全部取完,贪心地算出最多能取几次即可。

1A留念。


Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define maxn 100010
#define maxm 1000100
#define INF 0x7FFFFFFF
#define MAXLEN 1e18

using namespace std;

typedef long long LL;

int n, s, t, cur = -1;
int A[maxn], B[maxn], C[maxn];

int flow[maxn], q[maxn], preV[maxn];
LL dis[maxn];
bool vis[maxn], odd[maxn];

struct List{
    List *next, *rev;
    int obj, cap;
    LL cost;
}Edg[maxm], *head[maxn], *preE[maxn];

void Addedge(int a, int b, int c, LL d){
    Edg[++cur].next = head[a];
    Edg[cur].cap = c;
    Edg[cur].obj = b;
    Edg[cur].cost = d;
    Edg[cur].rev = Edg+(cur^1);
    head[a] = Edg+cur;
}

bool SPFA(){
    for(int i = s; i <= t; i++)  dis[i] = -MAXLEN, vis[i] = false;
    dis[s] = 0LL;
    int he = 0, ta = 0;
    q[0] = s;
    vis[s] = true;
    flow[s] = INF;
    while(he <= ta){
        int now = q[he%maxn];
        he ++;
        for(List *p = head[now]; p; p = p->next){
            int v = p->obj, c = p->cap;
            LL d = p->cost;
            if(!c)  continue;
            if(dis[now] + d > dis[v]){
                dis[v] = dis[now] + d;
                preV[v] = now;
                preE[v] = p;
                flow[v] = min(flow[now], c);
                if(!vis[v]){
                    vis[v] = true;
                    ta ++;
                    q[ta%maxn] = v;
                    if(dis[q[he%maxn]] < dis[q[ta%maxn]])  swap(q[he%maxn], q[ta%maxn]);
                }
            }
        }
        vis[now] = false;
    }
    return dis[t] != -MAXLEN;
}

int MinCostMaxFlow(){
    LL res = 0LL;
    int ans = 0;
    while(SPFA()){
        if(res + dis[t] * flow[t] < 0LL){  
            ans += res / (-dis[t]);
            break;
        }
        res += dis[t] * flow[t];
        ans += flow[t];
        for(int i = t; i != s; i = preV[i]){
            preE[i]->cap -= flow[t];
            preE[i]->rev->cap += flow[t];
        }
    }
    return ans;
}

bool Prime(int x){
    for(int i = 2; i * i <= x; i++)  if(x % i == 0)  return false;
    return true;
}

int main(){

    freopen("bzoj4514.in", "r", stdin);
    freopen("bzoj4514.out", "w", stdout);

    scanf("%d", &n);

    for(int i = 1; i <= n; i++)  scanf("%d", &A[i]);
    for(int i = 1; i <= n; i++)  scanf("%d", &B[i]);
    for(int i = 1; i <= n; i++)  scanf("%d", &C[i]);

    for(int i = 1; i <= n; i++){
        int temp = A[i], res = 0;
        for(int j = 2; j * j <= temp; j++){
            while(temp % j == 0){
                res ++;
                temp /= j;
            }
        }
        if(temp != 1)  res ++;
        if(res & 1)  odd[i] = true;
        else  odd[i] = false;
    }


    s = 1;  t = s + n + 1;
    for(int i = s; i <= t; i++)  head[i] = NULL;

    for(int i = 1; i <= n; i++){
        if(odd[i]){
            Addedge(s, s+i, B[i], 0LL);
            Addedge(s+i, s, 0, 0LL);
        }
        else{
            Addedge(s+i, t, B[i], 0LL);
            Addedge(t, s+i, 0, 0LL);
        }
    }   

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++){
            if(odd[i] && !odd[j]){
                int temp1 = A[i], temp2 = A[j];
                if(temp1 < temp2)  swap(temp1, temp2);
                if(temp1 % temp2 == 0 && Prime(temp1/temp2)){
                    Addedge(s+i, s+j, INF, 1LL*C[i]*C[j]);
                    Addedge(s+j, s+i, 0, -1LL*C[i]*C[j]);           
                }
            }
        }

    printf("%d\n", MinCostMaxFlow());

    return 0;
} 

这里写图片描述

下次你路过,人间已无我。

——余光中

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值