BZOJ4514: [Sdoi2016]数字配对

来源:互联网 发布:大数据运维工程师简历 编辑:程序博客网 时间:2024/05/17 10:07

BZOJ4514: [Sdoi2016]数字配对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1101  Solved: 419
[Submit][Status][Discuss]

题解:
把所有的数字分解质因数,两个数字相除如果想等于一个质数,那么他们的分解质因数的个数一定只相差1,于是我们可以用数字分解成质因数个数的奇偶性来建出一个二分图,把奇数的建一条(S,i,b[i],0),把偶数的建一条(i,T,b[i],0)。
枚举二分图两个集合的点,然后判断哪些奇偶性不同的点之间只差一个质因数,如果只差一个就连一条(i,j,inf,c[i]*c[j]),然后就建完模了。(from,to,flow,value)
本题的目的是让流量最大且费用不小于零,这样我们用Spfa跑最长路,尽可能的去流流量,费用先递增然后递减,直到小于等于零的时候我们结束费用流就可以了,最大流量即我们所能配对配最多的个数,流过去一个流量相当于配对一个
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;#define LL long long const int N=505;const int M=100005;const int inf=1e9;int n,tot[N],p[N][N],b[N],a[N];LL c[N],d[N],v[M],w[M];int to[M],nxt[M],from[M],lj[N],bef[N],cnt=1,S,T;void ins(int f,int t,LL ww,LL vv){cnt++,from[cnt]=f,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=ww,v[cnt]=vv;}void add(int f,int t,LL ww,LL vv){ins(f,t,ww,vv),ins(t,f,0,-vv);}bool Check(int x,int y){bool flag=false;int len=max(tot[x],tot[y]),lx=1,ly=1;while(lx<=len&&ly<=len){if(p[x][lx]!=p[y][ly]){if(flag==true) return false; if(tot[x]<tot[y]) ly++;else lx++;flag=true;}else lx++,ly++;}return true;}void Build(){S=0,T=n+1;for(int i=1;i<=n;i++){if(tot[i]%2==1) add(S,i,b[i],0);else add(i,T,b[i],0);} for(int i=1;i<=n;i++)if(tot[i]%2==1)for(int j=1;j<=n;j++) if(tot[j]%2==0&&abs(tot[i]-tot[j])==1)if(Check(i,j)) add(i,j,inf,c[i]*c[j]);}queue<int>Q;bool inq[N];bool Spfa(){for(int i=S;i<=T;i++) d[i]=-1e15;d[S]=0;Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();inq[x]=false;for(int i=lj[x];i;i=nxt[i])if(w[i]&&d[to[i]]<d[x]+v[i]){d[to[i]]=d[x]+v[i];bef[to[i]]=i;if(!inq[to[i]]){Q.push(to[i]);inq[to[i]]=true;}}}if(d[T]!=-1e15) return true;return false;}LL Exflow(){LL ans=0,ret=0;while(Spfa()){LL flow=inf;for(int i=T;i!=S;i=from[bef[i]]) flow=min(flow,w[bef[i]]);for(int i=T;i!=S;i=from[bef[i]]) w[bef[i]]-=flow,w[bef[i]^1]+=flow;if(ans+d[T]*flow<0) {ret+=ans/(-d[T]);break;}else ans+=d[T]*flow,ret+=flow;}return ret;}int main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);int x=a[i];for(int j=2;j<=sqrt(x);j++)if(x%j==0)while(x%j==0) p[i][++tot[i]]=j,x/=j;if(x>1) p[i][++tot[i]]=x;}for(int i=1;i<=n;i++) scanf("%d",&b[i]);for(int i=1;i<=n;i++) scanf("%lld",&c[i]);Build();printf("%lld",Exflow());} 

Description

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

Input

第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。

Output

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

Sample Input

3
2 4 8
2 200 7
-1 -2 1

Sample Output

4

HINT

 n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5

Source

鸣谢Menci上传

[Submit][Status][Discuss]
0 0
原创粉丝点击