题目描述

有 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。

输出

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

样例输入

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

样例输出

4

提示

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

有数量上限、有价值,显然费用流,因为题目要求费用不小于$0$,所以用最大费用最大流。将每个点拆成两个点$i$和$i'$,分别与源点和汇点连边,流量为$b[i]$、费用为$0$。枚举任意两个数判断是否能匹配。因为$i$与$j$能匹配,$j$就能与$i$匹配,所以将$i$与$j'$连边、$j$与$i'$连边,流量为$INF$、费用为$-c[i]*c[j]$(因为跑最大费用最大流,边权取反)。每次$SPFA$找到一条增广路,如果加上之后答案满足要求就继续增广,否则就停止。因为一对数的匹配算了两次,所以最后答案除$2$即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define INF 1000000000000000ll
#define inf 1000000000
using namespace std;
int head[1000];
int next[100000];
int to[100000];
ll v[100000];
int c[100000];
int f[1000];
int from[100000];
int tot=1;
int S,T;
ll ans;
int n;
int A[300];
int B[300];
int C[300];
queue<int>q;
int vis[1000];
ll d[1000];
int maxflow;
void add(int x,int y,ll z,int w)
{
next[++tot]=head[x];
head[x]=tot;
to[tot]=y;
v[tot]=z;
c[tot]=w;
from[tot]=x;
next[++tot]=head[y];
head[y]=tot;
to[tot]=x;
v[tot]=-z;
c[tot]=0;
from[tot]=y;
}
bool result()
{
int now=T;
int flow=inf;
while(now!=S)
{
flow=min(flow,c[f[now]]);
now=from[f[now]];
}
if(ans+d[T]*flow<=0)
{
ans+=d[T]*flow;
maxflow+=flow;
}
else
{
maxflow+=fabs(ans)/fabs(d[T]);
return 1;
}
now=T;
while(now!=S)
{
c[f[now]]-=flow;
c[f[now]^1]+=flow;
now=from[f[now]];
}
return 0;
}
bool SPFA()
{
for(int i=1;i<=T;i++)
{
d[i]=INF;
}
d[S]=0;
q.push(S);
vis[S]=1;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i;i=next[i])
{
if(!c[i])
{
continue;
}
if(d[to[i]]>d[now]+v[i])
{
d[to[i]]=d[now]+v[i];
f[to[i]]=i;
if(!vis[to[i]])
{
q.push(to[i]);
vis[to[i]]=1;
}
}
}
}
return d[T]!=INF;
}
void find_max()
{
while(SPFA())
{
if(result())
{
break;
}
}
}
bool check(int x,int y)
{
if(x<y)
{
swap(x,y);
}
if(x%y)
{
return false;
}
int d=x/y;
for(int i=2;i*i<=d;i++)
{
if(d%i==0)
{
return false;
}
}
return true;
}
int main()
{
scanf("%d",&n);
S=2*n+1,T=S+1;
for(int i=1;i<=n;i++)
{
scanf("%d",&A[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&B[i]);
add(S,i,0,B[i]);
add(i+n,T,0,B[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&C[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(check(A[i],A[j]))
{
add(i,n+j,-1ll*C[i]*C[j],1<<30);
add(j,n+i,-1ll*C[i]*C[j],1<<30);
}
}
}
find_max();
printf("%d",maxflow/2);
}

BZOJ4514[Sdoi2016]数字配对——最大费用最大流的更多相关文章

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

    4514: [Sdoi2016]数字配对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 726  Solved: 309[Submit][Status ...

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

    题目 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×c ...

  3. bzoj4514: [Sdoi2016]数字配对(费用流)

    传送门 ps:费用流增广的时候费用和流量打反了……调了一个多小时 每个数只能参与一次配对,那么这就是一个匹配嘛 我们先把每个数分解质因数,记质因子总个数为$cnt_i$,那如果$a_i/a_j$是质数 ...

  4. 【bzoj4514】: [Sdoi2016]数字配对 图论-费用流

    [bzoj4514]: [Sdoi2016]数字配对 好像正常的做法是建二分图? 我的是拆点然后 S->i cap=b[i] cost=0 i'->T cap=b[i] cost=0 然后 ...

  5. BZOJ4514——[Sdoi2016]数字配对

    有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的 ...

  6. bzoj4514 [Sdoi2016]数字配对

    Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对 ...

  7. bzoj4514: [Sdoi2016]数字配对--费用流

    看了一眼题目&数据范围,觉得应该是带下界的费用流 原来想拆点变成二分图,能配对的连边,跑二分图,可行性未知 后来看到另外一种解法.. 符合匹配要求的数要满足:质因子的个数相差为1,且两者可整除 ...

  8. 【BZOJ4514】数字配对(费用流)

    题意: 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci× ...

  9. [SDOI2016]数字配对(费用流+贪心+trick)

    重点是如何找到可以配对的\(a[i]\)和\(a[j]\). 把\(a[i]\)分解质因数.设\(a[i]\)分解出的质因数的数量为\(cnt[i]\). 设\(a[i]\geq a[j]\) 那么\ ...

随机推荐

  1. 【C#复习总结】细说匿名方法

    1 前言 本系列会将[委托] [匿名方法][Lambda表达式] [泛型委托] [表达式树] [事件]等基础知识总结一下.(本人小白一枚,有错误的地方希望大佬指正) 系类1:细说委托 系类2:细说匿名 ...

  2. Contest1692 - 2019寒假集训第三十一场 UPC 11075 Problem D 小P的国际象棋

    非常简单的单点修改+区间加+区间查询.我用的是最近刚学的区间修改版本树状数组.  直接维护即可,注意修改后的单点值已经不是a[i],或者b[i],要通过区间查询求单点.不然是错的. 区间修改版本树状数 ...

  3. Python—sys模块介绍

    sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 sys.maxi ...

  4. mysql数据库和JDBC学习

    数据库概念: 数据库(Database)是按照数据结构来组织.存储和管理数据的建立在计算机存储设备上的仓库. ---------数据库服务器,mysql(管理) 数据库服务器---->N多库-- ...

  5. oracle服务端安装与配置

    从oracle官网下载oracle服务端的安装包. 下载下来是两个压缩文件,两个压缩文件都解压(缺一不可)到同一目录下,最后会得到一个database文件夹. 双击database文件夹下的setup ...

  6. CPU Cache 机制以及 Cache miss

    CPU体系结构之cache小结 1.What is cache? Cache是用来对内存数据的缓存. CPU要访问的数据在Cache中有缓存,称为“命中” (Hit),反之则称为“缺失” (Miss) ...

  7. C# Note33: 总结C# 6.0/7.0 新特性

    先注明,本文主体参考自:C# 6.0新特性 目前代码中使用了很多C#6.0的新特性,下面以Point类来做相关叙述: public class Point { public int X { get; ...

  8. 一个简单jpa例子

    package com.example.demo.entity; import javax.persistence.*; /*使用jpa注解配置映射关系*/ /*告诉jpa这是一个实体类和(数据表映射 ...

  9. RN 实战 & React Native 实战

    RN 实战 & React Native 实战 https://abc.xgqfrms.xyz/react-native-docs/ 0.59 https://github.com/xgqfr ...

  10. Python2.7从入门到精通

    快速入门 1.程序输出print语句 (1)使用print语句可查看对象的值:在交互式解释器使用对象本身则输出此对象的字符串表示: (2)使用print语句调用str()显示对象:在交互式解释器使用对 ...