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

输出

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

样例输入

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;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/7148738.html