题目描述
有 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
题解
费用流
先预处理出什么样的两个数能够配对,易证:两个数能够配对,当且仅当它们是倍数关系,且质因子数差1。
所以对每个数进行质因数分解,然后两两之间判断即可(这样会比暴力判断快一些)。
然后如果没有“在获得的价值总和不小于 0 的前提下”,那么就是网络流傻*题,并且是二分图模型,S与含有奇数个质因子的数连边,含有偶数个质因子的数与T连边,能够配对的数两两连边,跑最大流即可。
那么如果有这个条件呢?肯定是先使用费用流,然后不断跑最大费用流,知道费用要减小到零以下时停止,判断一下这些费用可以满足多少流量,再加到答案中并跳出循环即可。由于最大费用流的贪心原理,易知这样做一定是正确的。
注意费用和距离要开long long。
#include <cstdio> #include <cstring> #include <queue> #define N 210 #define M 40010 using namespace std; typedef long long ll; const int inf = 1 << 30; queue<int> q; ll cost[M] , dis[N]; int a[N] , b[N] , val[M] , c[N] , v[N] , head[N] , to[M] , next[M] , cnt = 1 , s , t , from[N] , pre[N]; void add(int x , int y , int v , ll c) { to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt; } bool spfa() { int x , i; memset(from , -1 , sizeof(from)); memset(dis , 0x80 , sizeof(dis)); dis[s] = 0 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) if(val[i] && dis[to[i]] < dis[x] + cost[i]) dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]); } return ~from[t]; } int query() { int i , ans = 0 , k; ll cc = 0; while(spfa()) { k = inf; for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]); if(k * dis[t] + cc < 0) return ans + (-cc) / dis[t]; ans += k , cc += k * dis[t]; for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k; } return ans; } int main() { int n , i , j , tmp; scanf("%d" , &n) , s = 0 , t = n + 1; for(i = 1 ; i <= n ; i ++ ) { scanf("%d" , &a[i]); for(j = 2 , tmp = a[i] ; j * j <= tmp ; j ++ ) if(tmp % j == 0) while(tmp % j == 0) tmp /= j , v[i] ++ ; if(tmp != 1) v[i] ++ ; } for(i = 1 ; i <= n ; i ++ ) { scanf("%d" , &b[i]); if(v[i] & 1) add(s , i , b[i] , 0); else add(i , t , b[i] , 0); } for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &c[i]); for(i = 1 ; i <= n ; i ++ ) if(v[i] & 1) for(j = 1 ; j <= n ; j ++ ) if((v[j] - v[i] == 1 && a[j] % a[i] == 0) || (v[i] - v[j] == 1 && a[i] % a[j] == 0)) add(i , j , inf , (ll)c[i] * c[j]); printf("%d " , query()); return 0; }