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

4514: [Sdoi2016]数字配对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 726  Solved: 309
[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上传

Solution

二分图建图方式,分析一下

如果$a[i]$和$a[j]$互质,不妨把它们分解质因子,互质即质因子约去后的质因子互质,或者只剩一个质因子

所以考虑先预处理 线性筛质数,再质因数分解,含有奇数个质因子的与含有偶数个质因子的分列两排

如果满足$a[i]$和$a[j]$可连($a[i]$为质因数为奇的$a[j]$为质因数为偶的),则由$i--->j$连容量$inf$,费用$c[i]*c[j]$

由$S--->odd[i]$连容量$b[odd[i]]$,费用为$0$;同理$even[i]--->T$连容量$b[even[i]]$费用$0$

然后跑最大费用最大流,因为最大费用最大流每次增广的路径费用严格不下降,所以,贪心的一直跑到总费用$<=0$的时候,单独判断一下即可

容易出问题的细节:

1.注意开longlong...

2.Inf的范围要注意(习惯性的开0x7fffffff导致WA5组...)

3.不能暴力筛和暴力分解,时间复杂度不科学

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 1010
#define maxm 100010
int n,a[maxn],b[maxn];long long c[maxn]; bool f=0;
struct EdgeNode{int next,cap,to,from;long long cost;}edge[maxm];
int head[maxn],cnt=1;
void add(int u,int v,int w,long long c)
{
    cnt++;
    edge[cnt].to=v;edge[cnt].from=u;edge[cnt].cap=w;edge[cnt].cost=c;edge[cnt].next=head[u];head[u]=cnt;
}
void insert(int u,int v,int w,long long c) {add(u,v,w,c); add(v,u,0,-c);}
int from[maxn],S,T; long long dis[maxn]; bool mark[maxn];
#define inf 10000000000000LL
bool spfa()
{
    queue<int>q;
    for (int i=S; i<=T; i++) dis[i]=-inf; memset(from,0,sizeof(from));
    q.push(S); mark[S]=1; dis[S]=0;
    while (!q.empty())
        {
            int now=q.front(); q.pop(); mark[now]=0;
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]<dis[now]+edge[i].cost)
                    {
                        dis[edge[i].to]=dis[now]+edge[i].cost; from[edge[i].to]=i;
                        if (!mark[edge[i].to])
                            q.push(edge[i].to),mark[edge[i].to]=1;
                    }
        }
    return dis[T]!=-inf;
}
long long Cost; int Flow;
void MaxCostFlow()
{
    int flow=inf;
    for (int i=from[T]; i; i=from[edge[i].from])
        flow=min(flow,edge[i].cap);

    if ((long long)Cost+flow*dis[T]>=0LL)
        {
            for (int i=from[T]; i; i=from[edge[i].from])
                edge[i].cap-=flow,edge[i^1].cap+=flow;
            Flow+=flow; Cost+=dis[T]*flow;

        }
    else
        Flow+=abs(Cost/dis[T]),Cost-=(Cost/dis[T])*dis[T],f=1;
   // Flow+=flow;
    //printf("%d %I64d %I64d\n",Flow,Cost,dis[T]);
}
int prime[35001],tot; bool flag[35001];
void Prework()
{
    flag[1]=1;
    for (int i=2; i<=32000; i++)
        {
            if (!flag[i]) prime[++tot]=i;
            for (int j=1; j<=tot&&i*prime[j]<=32000; j++)
                flag[i*prime[j]]=1;
        }
}
int odd[maxn],even[maxn],ot,et;
bool check(int x,int y)
{
    if (x%y && y%x || !x || !y) return 0;
    int tmp=max(x/y,y/x);
    for (int i=1; i<=tot&&prime[i]<tmp; i++)
        if (tmp%prime[i]==0) return 0;
    return 1;
}
void Build()
{
    Prework();
    S=0,T=n+1;
    for (int i=1,ct=0; i<=n; i++,ct=0)
        {
            for (int j=1,tmp=a[i]; j<=tot; j++,tmp=a[i])
                while (tmp%prime[j]==0) tmp/=prime[j],ct++;
                    //else if (prime[j]>tmp) break;
            if (ct&1) odd[++ot]=i; else even[++et]=i;
        }
    for (int i=1; i<=ot; i++)
        for (int j=1; j<=et; j++)
            if (check(a[odd[i]],a[even[j]]))
                insert(odd[i],even[j],inf,c[odd[i]]*c[even[j]])/*,printf("%d-->%d:%d/%I64d\n",odd[i],even[j],inf,c[odd[i]]*c[even[j]])*/;
    for (int i=1; i<=ot; i++) insert(S,odd[i],b[odd[i]],0LL)/*,printf("%d-->%d:%d/%I64d\n",S,odd[i],b[odd[i]],0LL)*/;
    for (int i=1; i<=et; i++) insert(even[i],T,b[even[i]],0LL)/*,printf("%d-->%d:%d/%I64d\n",even[i],T,b[even[i]],0LL)*/;
    //printf("%d %d %d\n",ot,et,cnt);
}
int main()
{
//    freopen("menci_pair.in","r",stdin);
//    freopen("menci_pair.out","w",stdout);
    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]=(long long)read();
    Build();
    while (spfa() && !f) MaxCostFlow();
    printf("%d\n",Flow);
    return 0;
}

当时唯一想出和出题人一模一样的解法的题,但是最后不知道莫名其妙的写错了什么地方,结果连暴力分都不到....(回来之后这么久才想起来重写一遍..)

时间: 2024-10-10 13:13:07

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

【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

BZOJ 1927 星际竞速(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1927 题意:一个图,n个点.对于给出的每条边 u,v,w,表示u和v中编号小的那个到编号大的那个的时间为w.另外有n个值Ai,表示从任何一个点到达i点的时间为Ai.初始时你在n个点之外的一个 点上,我们称其为初始点B.要求从B出发,遍历n个点每个点一次,求最小时间.显然开始你只能使用Ai从B到达n个点中的某个点,因为B到n个点中没有其 他的边. 思路:因为最后停在了某个点上,那么从B出

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

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

题目 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. 输入格式 第一行一个整数 n. 第二行 n 个整数 a1.a2.--.an. 第三行 n 个整数 b1.b2.--.bn. 第四行 n 个整数 c1.c2.--.cn. 输出格式

BZOJ 2424: [HAOI2010]订货(最小费用最大流)

最小费用最大流..乱搞即可 ------------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #define rep( i, n ) for( int i = 0; i <

BZOJ 1070: [SCOI2007]修车(最小费用最大流)

建图很神奇..建完图其实就是裸的费用流了.. -------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #include<queue> #define rep(i,n) for(int i

bzoj 1927 星际竞速 —— 最小费用最大流

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1927 首先注意到这是个DAG: 考虑每个点从哪里来,可以是瞬移来的,也可以是从某个点走过来的,而从每个点走出去只能用一次: 所以拆点,i 表示从这个点走出去,n+i 表示来到这个点: 建图: 1.瞬移:S 向 n+i 连边权 a[i],流量1的边 2.走过来:如果 i 能走到 j,那么 i 向 n+j 连边权 w,流量1的边 然后 S 向 i 连边权0,流量1的边,表示一个点只能走出去一次

图论(费用流):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 的价值. 一个数字只能参与一次配对,可以不参与配对. 在

BZOJ 4514: [Sdoi2016]数字配对

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