BZOJ4514: [Sdoi2016]数字配对
Time Limit: 10 Sec
Memory Limit: 128 MB
Submit: 1101
Solved: 419
题解:
把所有的数字分解质因数,两个数字相除如果想等于一个质数,那么他们的分解质因数的个数一定只相差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
2 4 8
2 200 7
-1 -2 1
Sample Output
4
HINT
n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5