您的位置:首页 > 其它

【BZOJ4514】数字配对,费用流

2016-04-13 20:16 274 查看
传送门

题面:



写在前面:网络流练习太少……

思路:费用流,最大或最小随意,看你给费用的符号,建图的话是把数分成两部分,分别是奇数个质因子和偶数个质因子,然后通过题目给出的关系连边(分部分的原因是形成二分图,从而分别向源点和汇点连边),费用为(+-)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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: