[SDOI2016][bzoj4514] 数字配对 [费用流]
题面
思路
一个数字能且只能匹配一次
这引导我们思考:一次代表什么?代表用到一定上限(b数组)就不能再用,同时每用一次会产生价值(c数组)
上限?价值?网络流!
把一次匹配设为一点流量,那产生的价值不就是费用了吗?
我们考虑把一种数字抽象成一个点,可以匹配的数字之间连边,费用为c[i]*c[j],流量上限为.....
等等,流量上限怎么设?
而且还有一个问题:这里的匹配是双向的,虽然可以$O\left(n^2\right)$求出所有匹配对,但是网络流要求是单向边啊!
别急,我们先来分析一下两个满足匹配条件的数,有什么性质
设$i=p\ast j$,其中p是一个质数
那我们考虑$i$和$j$的质因数分解,会发现:它们俩分解出的质因数个数之间正好差一!
这说明了什么?
这说明匹配只有可能在质因数个数奇偶性不同的数对之间存在,而如果根据质因数个数的奇偶性把数分成两组,那么所有边都在两组之间!
这是什么?二分图啊!
那么我们就可轻易把每条边定向成从奇数侧到偶数侧了!
接下来的事就简单了:我们建立超级源S和超级汇T,从S连边到所有质因数个数为奇数的点i,费用为0,容量为b[i],质因数个数为偶数的点连到T,类似
这样,我们也一同限制了每个点最多流出去不超过b[i]的流量,也就是不发生超过b[i]次和这个数字有关的匹配
因此对于原图中的可行匹配,只要连边,费用为c[i]*c[j],流量上限inf
跑最大费用最大流......等等好像不行?这道题是要求费用非负时的最大流量啊......
别急,我们来贪心一波
我们每次在图中做一个spfa,找到费用最大(最长)的增广路,设它的总长度(费用)为maxn,同时设当前总费用为ans
如果maxn<-ans,那么即使加上1的流量,总费用也负数了,这个时候结束循环,输出总流量flow即可
否则,如果maxn>0,那么非常高兴,我们随便加,流量越多越好
如果maxn<0,那么也没有问题,我们只要令流的流量为$min(limit,ans/(-maxn))$,其中limit为当前增广路的流量上限
这样一直循环,直到因为上面的原因跳出,或者图不连通了为止,输出总流量flow,就是最大匹配数了
贪心的证明很显然,我们每次都是取最优走,而且后面的决策肯定没有我优,就证完了
Code:
写的时候注意细节啊......这题细节贼多,一不小心就除0或者mod0了,而且实现分解质因数的时候注意,如果一个数x到了sqrt(x)都还没有一个质因数,那么它肯定是个质数
因此我们只要筛1e5的素数就够了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define inf 1e15
#define ll long long
using namespace std;
inline ll read(){
ll re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
ll cnt=-1,ans,flow,first[210],dis[210],vis[210],limit[210],pre[210];
struct edge{
ll to,next,w,cap;
}a[100010];
inline void add(ll u,ll v,ll w,ll cap){
a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
bool spfa(ll s,ll t){
ll q[5010]={0},head=0,tail=1,u,v,w,i;
for(i=s;i<=t;i++) dis[i]=-inf;
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));memset(limit,0,sizeof(limit));
q[0]=s;vis[s]=1;dis[s]=0;limit[s]=inf;
while(head<tail){
u=q[head++];vis[u]=0;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;w=a[i].w;
if(a[i].cap&&(dis[v]<dis[u]+w)){//注意是最长路
dis[v]=dis[u]+w;
limit[v]=min(limit[u],a[i].cap);
pre[v]=i;
if(!vis[v]) q[tail++]=v,vis[v]=1;
}
}
}
return dis[t]!=-inf;
}
ll n,A[210],B[210],C[210],col[210];
ll tot=0,pri[100010],v[100010]={0};
void init(){//线筛
ll i,j,k;v[1]=1;
for(i=2;i<=100000;i++){
if(!v[i]) pri[++tot]=i;
for(j=1;j<=tot;j++){
k=i*pri[j];if(k>100000) break;
v[k]=1;
if((i%pri[j])==0) break;
}
}
}
ll cntprime(ll x){
ll re=0,c=1;
while(x>1&&c<=tot){
while((x%pri[c])==0) x/=pri[c],re++;
c++;
}
if((c==tot+1)&&(x>1)) return 1;//处理特殊情况
return re;
}
int main(){
memset(first,-1,sizeof(first));
ll i,j;init();
n=read();
for(i=1;i<=n;i++) A[i]=read(),col[i]=cntprime(A[i]);
for(i=1;i<=n;i++){
B[i]=read();
if(col[i]%2) add(0,i,0,B[i]);
else add(i,n+1,0,B[i]);
}
for(i=1;i<=n;i++) C[i]=read();
for(i=1;i<=n;i++){
for(j=i+1;j<=n;j++){
if((((A[i]%A[j])==0)&&(col[i]==col[j]+1))||(((A[j]%A[i])==0)&&(col[j]==col[i]+1))){
if(col[i]%2) add(i,j,C[i]*C[j],inf);
else add(j,i,C[i]*C[j],inf);
}
}
}
ll tmp,u;
while(1){
if(!spfa(0,n+1)) break;
if(dis[n+1]+ans<0) break;
if(dis[n+1]>=0) tmp=limit[n+1];//注意这里>=不要写成>......我被这个坑了1h啊啊啊啊
else tmp=min(limit[n+1],ans/(-dis[n+1]));
ans+=dis[n+1]*tmp;flow+=tmp;
for(u=n+1;~pre[u];u=a[pre[u]^1].to){
a[pre[u]].cap-=tmp;a[pre[u]^1].cap+=tmp;
}
}
printf("%lld\n",flow);
}
[SDOI2016][bzoj4514] 数字配对 [费用流]的更多相关文章
- 【BZOJ4514】【SDOI2016】数字配对 [费用流]
数字配对 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ...
- [bzoj4514]数字配对[费用流]
今年SDOI的题,看到他们在做,看到过了一百多个人,然后就被虐惨啦... 果然考试的时候还是打不了高端算法,调了...几天 默默地yy了一个费用流构图: 源连所有点,配对的点连啊,所有点连汇... 后 ...
- 【BZOJ4514】[Sdoi2016]数字配对 费用流
[BZOJ4514][Sdoi2016]数字配对 Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ...
- bzoj4514: [Sdoi2016]数字配对--费用流
看了一眼题目&数据范围,觉得应该是带下界的费用流 原来想拆点变成二分图,能配对的连边,跑二分图,可行性未知 后来看到另外一种解法.. 符合匹配要求的数要满足:质因子的个数相差为1,且两者可整除 ...
- BZOJ 4514: [Sdoi2016]数字配对 [费用流 数论]
4514: [Sdoi2016]数字配对 题意: 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数 ...
- BZOJ.4514.[SDOI2016]数字配对(费用流SPFA 二分图)
BZOJ 洛谷 \(Solution\) 很显然的建二分图后跑最大费用流,但有个问题是一个数是只能用一次的,这样二分图两部分都有这个数. 那么就用两倍的.如果\(i\)可以向\(j'\)连边,\(j\ ...
- 【BZOJ 4514】[Sdoi2016]数字配对 费用流
利用spfa流的性质,我直接拆两半,正解分奇偶(妙),而且判断是否整除且质数我用的是暴力根号,整洁判断质数个数差一(其他非spfa流怎么做?) #include <cstdio> #inc ...
- 4514: [Sdoi2016]数字配对 费用流
链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4514 思路 EK直接贪心做 <0的时候加上剩余返回 二分图a->b的时候 把b- ...
- [SDOI2016 Round1] 数字配对
COGS 2221. [SDOI2016 Round1] 数字配对 http://www.cogs.pro/cogs/problem/problem.php?pid=2221 ★★★ 输入文件:m ...
随机推荐
- 2017.12.19 Java包的静态导入import static和import的区别
import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com-..ClassName;而静态导入是这样:import static com-..ClassN ...
- 安装Ubuntu桌面环境后只能Guest登录的解决办法
1.安装Ubuntu桌面环境后,登录界面只显示了Guest 2.在登录界面按住crtl+shift+F1,进入tty模式 3.输入sudo -s进入root模式 4.输入vi /etc/lightdm ...
- 小波变换(wavelet transform)的通俗解释(一)
小波变换 小波,一个神奇的波,可长可短可胖可瘦(伸缩平移),当去学习小波的时候,第一个首先要做的就是回顾傅立叶变换(又回来了,唉),因为他们都是频率变换的方法,而傅立叶变换是最入门的,也是最先了解的, ...
- WordPress免费清新响应式博客/杂志/图片三合一主题Nana
WordPress免费清新响应式博客/杂志/图片三合一主题Nana 一.主题安装须知 1.本主题必须安装文章点击统计插件:WP-PostViews,可以直接在后台插件→安装插件中直接搜索安装官方最新版 ...
- el-upload控件一次接口请求上传多个文件
el-upload组件默认情况下上传多少个文件就会请求多少次上传接口,如何一次上传多个文件而不必多次请求上传接口呢?直接看代码 html <el-upload :action="act ...
- node的webserver模板
const express = require('express'); const swig =require('swig'); const fs = require('fs'); //创建服务器 c ...
- 牛客NOIP提高组R1 C保护(主席树)
题意 题目链接 Sol Orz lyq 我们可以把一支军队(u, v)拆分为两个(u, lca)和(v, lca) 考虑一个点x,什么时候军队对它有贡献,肯定是u或v在他的子树内,且lca在他的子树外 ...
- Linux运维企业架构项目实战系列
Linux运维企业架构项目实战系列 项目实战1—LNMP的搭建.nginx的ssl加密.权限控制的实现 项目实战2—LVS.nginx实现负载均衡系列2.1 项目实战2.1—实现基于LVS负载均衡集群 ...
- CentOS7安装Nginx、MySQL、PHP
之前才网上找了好多文章,但是配置总会出错,后来傻傻的发现官方文档都有的,当然配合网上文章说明更好,因此本文只说一个大概 安装PHP 官方配置 配置用户和用户组,需要有根目录权限 vim /usr/lo ...
- python爬虫的基本思路
爬虫:请求网站并提取数据的自动化程序. 流程: 发送请求 -> 获取数据 -> 解析数据 -> 存储数据