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

     

  • 相关阅读:
    【HDU】4092 Nice boat(多校第四场1006) ——线段树 懒惰标记 Prime
    【POJ】2528 Mayor's posters ——离散化+线段树 Prime
    【HDU】1754 I hate it ——线段树 单点更新 区间最值 Prime
    C语言 linux环境基于socket的简易即时通信程序 Prime
    Java异常处理
    重载和重写的区别与联系
    SQL Server的优点与缺点
    Servlet基础
    C语言图形编程
    socket通讯,TCP,UDP,HTTP的区别
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7148738.html
Copyright © 2020-2023  润新知