【BZOJ4514】数字配对,费用流
2016-04-13 20:16
274 查看
传送门
题面:
![](https://img-blog.csdn.net/20160413201505961)
写在前面:网络流练习太少……
思路:费用流,最大或最小随意,看你给费用的符号,建图的话是把数分成两部分,分别是奇数个质因子和偶数个质因子,然后通过题目给出的关系连边(分部分的原因是形成二分图,从而分别向源点和汇点连边),费用为(+-)c[i]*c[j](正负号根据跑的是最大费用还是最小费用来决定),流量正无穷,然后两部分分别向源点汇点连边,费用0,流量为b[i]。
注意:
1.两部分连边时需要判断a[i]/a[j]是不是质数,Shallwe的live版程序每次O(a[i]/a[j]−−−−−−√)暴力求解,但当其取极限数据时可达到每次判断O(10^4.5),n2的循环次数不能承受,所以我们可以筛出1-10^4.5的质数,然后每次求a[i]/a[j]是否在这个范围内有质因子就可以判断其是否为质数了
2.费用流终止条件为没有可流的边或费用总和不满足要求,所以我们要记录每次跑完最小(大)费用后的费用总和,且当它要超出规定范围时,我们要把答案加上临界值再退出(比如跑完一次最小费用后,之前答案的费用总和为-4,而这次要增加的为2(流量)*4(本次的费用),那么我们不能全部跑完2个流量,但可以只跑1个流量,所以要ans+=之前的费用总和/本次费用)
3.注意long long,不然会被卡成狗
代码:
题面:
写在前面:网络流练习太少……
思路:费用流,最大或最小随意,看你给费用的符号,建图的话是把数分成两部分,分别是奇数个质因子和偶数个质因子,然后通过题目给出的关系连边(分部分的原因是形成二分图,从而分别向源点和汇点连边),费用为(+-)c[i]*c[j](正负号根据跑的是最大费用还是最小费用来决定),流量正无穷,然后两部分分别向源点汇点连边,费用0,流量为b[i]。
注意:
1.两部分连边时需要判断a[i]/a[j]是不是质数,Shallwe的live版程序每次O(a[i]/a[j]−−−−−−√)暴力求解,但当其取极限数据时可达到每次判断O(10^4.5),n2的循环次数不能承受,所以我们可以筛出1-10^4.5的质数,然后每次求a[i]/a[j]是否在这个范围内有质因子就可以判断其是否为质数了
2.费用流终止条件为没有可流的边或费用总和不满足要求,所以我们要记录每次跑完最小(大)费用后的费用总和,且当它要超出规定范围时,我们要把答案加上临界值再退出(比如跑完一次最小费用后,之前答案的费用总和为-4,而这次要增加的为2(流量)*4(本次的费用),那么我们不能全部跑完2个流量,但可以只跑1个流量,所以要ans+=之前的费用总和/本次费用)
3.注意long long,不然会被卡成狗
代码:
#include<cstdio> #include<iostream> #include<queue> #include<cstring> #define LL long long #define Inf 10000000000000LL using namespace std; int n,tot=1,s,t; int a[204],b[204],first[204],One[204],Two[204],up[204]; int prime[10010],ans; LL dis[204],maxn,c[204]; bool vis[32004]; struct edge { int u,v,w,next; LL cost; }e[50000]; queue<int>q; void add(int x,int y,int z,LL f) { e[++tot].u=x; e[tot].v=y; e[tot].w=z; e[tot].cost=f; e[tot].next=first[x]; first[x]=tot; } void init()//筛出1-32000的质数 { for (int i=2;i<=32000;i++) { if (!vis[i]) prime[++prime[0]]=i; for (int j=1;j<=prime[0];j++) { if (i*prime[j]>32000) break; vis[i*prime[j]]=1; if (i%prime[j]==0) break; } } } bool pd(int i,int j) { if (!a[i]||!a[j]||a[i]%a[j]&&a[j]%a[i]) return 0; int p=max(a[i]/a[j],a[j]/a[i]); for (int k=1;k<=prime[0];k++) if (prime[k]>=p) break; else if (p%prime[k]==0) return 0; return 1; } bool spfa() { memset(dis,127,sizeof(dis)); memset(up,0,sizeof(up)); dis[s]=0; vis[s]=1; q.push(s); bool yes=0; while (!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for (int i=first[x];i;i=e[i].next) if (e[i].w&&dis[e[i].v]>dis[x]+e[i].cost) { dis[e[i].v]=dis[x]+e[i].cost; up[e[i].v]=i; if (!vis[e[i].v]) vis[e[i].v]=1,q.push(e[i].v); if (e[i].v==t) yes=1; } } if (!yes) return 0; else return 1; } bool flow() { int minn=Inf; for (int i=up[t];i;i=up[e[i].u]) minn=min(minn,e[i].w); if (maxn+dis[t]*minn<=0) { for (int i=up[t];i;i=up[e[i].u]) e[i].w-=minn, e[i^1].w+=minn; ans+=minn; maxn+=dis[t]*minn; return 1; } else {ans-=(maxn/dis[t]);return 0;} } main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) scanf("%d",&b[i]); for (int i=1;i<=n;i++) scanf("%lld",&c[i]); init(); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++) { int k,sum=0; for (int j=1;j<=prime[0];j++) { k=a[i]; while (k%prime[j]==0) k/=prime[j],sum++; } if (sum&1) One[++One[0]]=i; else Two[++Two[0]]=i; } for (int i=1;i<=One[0];i++) for (int j=1;j<=Two[0];j++) if (pd(One[i],Two[j])) add(One[i],Two[j],Inf,-c[One[i]]*c[Two[j]]), add(Two[j],One[i],0,c[One[i]]*c[Two[j]]); s=n+1;t=n+2; for (int i=1;i<=One[0];i++) add(s,One[i],b[One[i]],0), add(One[i],s,0,0); for (int i=1;i<=Two[0];i++) add(Two[i],t,b[Two[i]],0), add(t,Two[i],0,0); while (spfa()&&flow()); printf("%d",ans); }
相关文章推荐
- 四大浏览器内核简介
- C语言输出控制符
- iOS应用逆向工程
- 杭电1061
- VMware虚拟机从一台电脑转移复制到另一台电脑的方法
- Hibernate配置C3P0支持
- html5笔记—— 插入视频音频
- springmvc tomcat转weblogic
- Java中怎么切换窗口
- nodeJS ejs模板引擎 片段视图+视图助手
- powerdesigner
- 门面模式
- C# 结构体直接赋值的问题
- COCI 2015/2016 Day 8 PROKLETNIK
- 大端模式小端模式
- git学习笔记
- java 获取 jar 包内文件列表
- Redis和Memcached的区别
- 矩阵的快速幂
- Redis和Memcached的区别