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

题意

给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 */

你可能感兴趣的:(【bzoj4514】[Sdoi2016]数字配对 费用流)