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

本文探讨了一种复杂数值配对问题的解决方法,通过将问题转化为二分图模型,利用最大流算法求解最优解,实现权值最大化的目标。详细介绍了问题背景、算法原理及具体步骤,并通过实例演示了解题过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给n个数,他们互不相同,第i个数是a[i],有b[i]个,权值为c[i]。两个数可以配对当且仅当a[i]是a[j]的倍数且a[i]/a[j]是素数,它们配对所产生的权值是c[i]*c[j]。每次配对消耗一个数。求权值不小于0的情况下最大配对数量。

样例输入

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

样例输出

4

数据范围

30%:暴力,b[i]=1
60%:c[i]=0
100%:n<=200,a[i]<=10^9,b[i]<=10^5,c[i]<=10^5


我竟然没看出来是二分图……

唯一分解后的指数之和的奇偶性可以自然而然的二分图染色,想到这点就很容易做了……

然后可配对点之间连边,流量INF。源点向指数之和是奇数的点连容量是b[i]的边,偶数的点向汇点连是b[i]的边,然后跑最大流就50分了。

顺着这个思路就很好想……既然要有权值,肯定在INF边上加上c[i]*c[j]的权值。因为跑最大费用流的时候是从费用最大的路径先增广,所以费用是单调不升的,所以在费用大于0且最小的时候的流量就是答案。

longlong坑爹。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;

typedef long long LL;
const int SZ = 1000010;
const LL INF = 100000000000000010;

int n;

int head[SZ],nxt[SZ];

struct edge{
    int f,t;
    LL d,c;
}l[SZ];

void build(int f,int t,LL d,LL c)
{
    static int tot = 1;
    l[++ tot].f = f; l[tot].t = t; l[tot].d = d; l[tot].c = c;
    nxt[tot] = head[f];
    head[f] = tot;
}

void insert(int f,int t,LL d,LL c)
{
    build(f,t,d,c); build(t,f,0,-c);
}

bool use[SZ];
LL dist[SZ];
int pre[SZ];
queue<int> q;

bool spfa(int s,int e)
{
    memset(dist,-128,sizeof(dist));
    dist[s] = 0;
    use[s] = 1;
    q.push(s);
    while(q.size())
    {
        int u = q.front(); q.pop();
        use[u] = 0;
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(l[i].d && dist[v] < dist[u] + l[i].c)
            {
                dist[v] = dist[u] + l[i].c;
                pre[v] = i;
                if(!use[v])
                {
                    use[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return dist[e] < -INF ? false : true;
}

LL cost = 0;

LL dfs(int s,int e)
{
    LL flow = INF;

    for(int i = pre[e];i;i = pre[l[i].f])
        flow = min(flow,l[i].d);

    if(cost + flow * dist[e] < 0) flow = -cost / dist[e];   
    cost += flow * dist[e];

    for(int i = pre[e];i;i = pre[l[i].f])
        l[i].d -= flow,l[i ^ 1].d += flow;
    return flow;
}

LL EK(int s,int e)
{
    LL ans = 0;
    while(spfa(s,e))
    {
        LL tmp = dfs(s,e);
        if(tmp == 0) break;
        ans += tmp;
    }
    return ans;
}

int a[SZ];
LL c[SZ],b[SZ];

bool jojo[SZ];

int zys(int n)
{
    int x = n;
    int ans = 0;
    for(int i = 2;i <= sqrt(x);i ++)
    {
        while(n % i == 0)
        {
            n /= i;
            ans ++;
        }
    }
    if(n != 1)
        ans ++;
    return ans;
}

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

int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
    for(int i = 1;i <= n;i ++) scanf("%lld",&b[i]);
    for(int i = 1;i <= n;i ++) scanf("%lld",&c[i]);

    for(int i = 1;i <= n;i ++) jojo[i] = zys(a[i]) % 2;

    int s = n + 1,e = n + 2;

    for(int i = 1;i <= n;i ++)
    {
        if(jojo[i] == 1)
            insert(s,i,b[i],0);
        else
            insert(i,e,b[i],0);
    }

    for(int i = 1;i <= n;i ++)
    {
        for(int j = i + 1;j <= n;j ++)
        {
            if(jojo[i] ^ jojo[j])
            {
                if(a[i] % a[j] == 0 && ispri(a[i] / a[j]))
                {
                    if(jojo[i]) insert(i,j,INF,c[i] * c[j]);
                    else insert(j,i,INF,c[i] * c[j]);
                }
                if(a[j] % a[i] == 0 && ispri(a[j] / a[i]))
                {
                    if(jojo[i]) insert(i,j,INF,c[i] * c[j]);
                    else insert(j,i,INF,c[i] * c[j]);                   
                }
            }
        }
    }

    printf("%lld\n",EK(s,e));

    return 0;
}
/*
3
2 4 8
2 200 7
-1 -2 1

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值