zoukankan      html  css  js  c++  java
  • 【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;
    }
    

     

  • 相关阅读:
    「SDOI2018」物理实验
    「SDOI 2018」战略游戏
    「CodeChef Dec13 REALSET」 Petya and Sequence 循环卷积
    关于微信卡券投放code接口报错原因
    composer update maatwebsite/excel 之后 在linux机子上出现500解决方案
    开启mysql 服务【window】
    thinkphp在linux上部署环境(500情况)
    如何推广微信小程序到企业微信
    linux 阿里云远程连接mysql
    php7以上 不支持mcrypt_module_open方法问题【微信开放平台】
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7148738.html
Copyright © 2011-2022 走看看