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

Posted lzdhydzzh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Bzoj4514][Sdoi2016]数字配对(费用流)相关的知识,希望对你有一定的参考价值。

4514: [Sdoi2016]数字配对


 

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2204  Solved: 865
[Submit][Status][Discuss]

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

 

Source


 

鸣谢Menci上传

 

分析:


通过这道题我明白了这类题要去构造二分图模型。。
把奇数个质因子 偶数个质因子的放两边,然后匹配。
一直跑费用流跑到变负就好。或者边权取反,跑到变正就好。
 
wa在了把局部变量和整体变量定义一样了,还得了70,下次注意点把。
 

AC代码:


 

# include <iostream>
# include <cstdio>
# include <cmath>
# include <cstring>
using namespace std;
typedef long long LL;
const int N = 212;
const LL Inf = 1e18;
int a[N],n,cnt,prime[32001],s,t,one[N],two[N],head[N],dt;
bool vis[32001];LL c[N],b[N];
struct Edge{
    int to,nex;LL res,cost;
}edge[N * N * 4];
void AddEdge(int u,int v,LL w,LL c)
{
    edge[dt] = (Edge){v,head[u],w,c};
    head[u] = dt++;
    edge[dt] = (Edge){u,head[v],0,-c};
    head[v] = dt++;
}
void shai()
{
    for(int i = 2;i <= 32000;i++)
    {
        if(!vis[i])prime[++cnt] = i;
        for(int j = 1;j <= cnt;j++)
        {
            if(i * prime[j] > 32000)break;
            vis[i * prime[j]] = true;
            if(i % prime[j] == 0)break;
        }
    }
}
void init(){shai();memset(head,-1,sizeof head);}
void divide()
{
    for(int i = 1;i <= n;i++)
    {
        int sum = 0,x = a[i];
        for(int j = 1;j <= cnt;j++)
        {
            if(prime[j] > x)break;
            while(x % prime[j] == 0)x /= prime[j],sum++;
        }
        if(sum & 1)one[++one[0]] = i,AddEdge(s,i,b[i],0);
        else two[++two[0]] = i,AddEdge(i,t,b[i],0);
    }
}
bool pd(int x,int y)
{
    if(!x || !y || (x % y && y % x))return false;
    int k = max(x / y,y / x);
    if(k == 1)return false;
    for(int i = 1;i <= cnt;i++)if(prime[i] >= k)break;
    else if(k % prime[i] == 0)return false;
    return true;
}
int que[N * 10000],pre[N];LL dis[N];
bool spfa()
{
    for(int i = s;i <= t;i++)vis[i] = 0,pre[i] = -1,dis[i] = Inf;
    int H = 0,T = 0,u;que[T++] = s;dis[s] = 0;
    while(H != T)
    {
        u = que[H++];vis[u] = false;
        for(int i = head[u];~i;i = edge[i].nex)if(edge[i].res && dis[edge[i].to] > dis[u] + edge[i].cost)
        {
            dis[edge[i].to] = dis[u] + edge[i].cost;
            pre[edge[i].to] = i;
            if(!vis[edge[i].to])vis[edge[i].to] = true,que[T++] = edge[i].to;
        }
     }
     return pre[t] != -1;
}
void Mcmf(LL &ans,LL &cost)
{
    ans = cost = 0;
    while(spfa())
    {
     LL tmp = Inf;
     for(int i = pre[t];~i;i = pre[edge[i ^ 1].to])
     tmp = min(tmp,edge[i].res);
     if(cost + dis[t] * tmp <= 0)
     {
         for(int i = pre[t];~i;i = pre[edge[i ^ 1].to])
         edge[i].res -= tmp,edge[i ^ 1].res += tmp;
         ans += tmp;
         cost += dis[t] * tmp;
     }
     else {ans -= (cost / dis[t]);return;}
    }
}
int main()
{
    scanf("%d",&n);init();s = 0;t = n + 1;
    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]);
    divide();
    for(int i = 1;i <= one[0];i++)
    {
        for(int j = 1;j <= two[0];j++)
        if(pd(a[one[i]],a[two[j]]))
        AddEdge(one[i],two[j],Inf,-c[one[i]] * c[two[j]]);
    }
    LL ans,cost;
    Mcmf(ans,cost);
    printf("%lld\n",ans);
}

 

以上是关于[Bzoj4514][Sdoi2016]数字配对(费用流)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

BZOJ4514: [Sdoi2016]数字配对

BZOJ4514SDOI2016数字配对 [费用流]

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

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