BZOJ4514: [Sdoi2016]数字配对
【传送门:BZOJ4514】
简要题意:
给出n种数a[i]和每种数的个数b[i],再给出c[i](1<=i<=n),如果a[i]和a[j]能配对当且仅当a[i]%a[j]==0&&a[i]/a[j]为质数,并且i和j配对的价值为c[i]*c[j],要求在总价值不小于0的情况下,求出最大配对数
题解:
跟机房的Hanks_o和Rose_max讨论了老久才做出来
最大费用最大流
首先怎么处理两个数是否匹配呢,数据规模太大了,显然不能直接判质数或者用线性筛存素数
我们发现,因为如果a[i]是a[j]的倍数,所以a[j]的质因数肯定在a[i]的质因数里面,那么只要a[i]的质因数减去a[j]的质因数为1时,证明a[i]/a[j]为质数
就用f[i]表示a[i]的质因数个数,可以用O(n*a[i]0.5)做出来
然后就是建边问题了,最初我们认为因为这并不是二分图,所以不能直接二分图转费用流
但是我们发现,如果a[i]能与a[j]匹配,则i连向j+n(避免重复),流量为无穷大,费用为c[i]*c[j],这时,我们再把j连向i+n,流量为无穷大,费用为c[i]*c[j]
为什么这么做?
因为呢,我们在讨论的时候发现,一旦i选择走向j+n的话,j一定也会选择走向i+n
那么就记录总流量,然后总流量/2就是答案了
然而上面的做法会超时,因为流量太大了,普通的费用流(其实是我们平时用的费用流)是一流量一流量的流的,这样太慢了
那么我们一旦找到一条增广路的时候就把这条增广路的流量榨干,注意,要判断总价值是否<0,如果总价值<0就退出费用流
为什么这样做是正确的?因为当我们找到这条增广路的时候,这条增广路上的总价值一定是最大的(我们找的就是最大的),一旦这条增广路流不完,那么说明其他增广路也肯定不能流
注意要加long long
这次总结了一个经验:一个良好的代码习惯是最重要的(就因为平时没注意,结果硬生生WA了3次)
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; typedef long long LL; struct node { int x,y,c,next,other; LL d; }a[410000];int len,last[410]; void ins(int x,int y,int c,LL d) { int k1=++len,k2=++len; a[k1].x=x;a[k1].y=y;a[k1].d=d;a[k1].c=c; a[k1].next=last[x];last[x]=k1; a[k2].x=y;a[k2].y=x;a[k2].d=-d;a[k2].c=0; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } LL d[410];int pre[410],pos[410]; bool v[410]; int list[410]; int st,ed; bool spfa() { bool bk=false; memset(d,-63,sizeof(d)); d[st]=0; memset(v,false,sizeof(v)); v[st]=true; list[1]=st; int head=1,tail=2; while(head!=tail) { int x=list[head]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&d[y]<d[x]+a[k].d) { d[y]=d[x]+a[k].d; pos[y]=x; pre[y]=k; if(v[y]==false) { v[y]=true; if(y==ed) bk=true; list[tail++]=y;if(tail==ed+1)tail=1; } } } head++;if(head==ed+1)head=1; v[x]=false; } return bk; } int f[210],A[210],B[210]; LL c[210]; int zys(int x) { int t=int(sqrt(double(x+1))),ans=0; for(int i=2;i<=t;i++) { while(x%i==0) { ans++; x/=i; } } if(x!=1) ans++; return ans; } LL ans,t; void Flow() { ans=0; while(spfa()) { int x; int mc=999999999; for(x=ed;x!=st;x=pos[x]) mc=min(mc,a[pre[x]].c); if(d[ed]<0) { LL dd=abs(d[ed]); if(ans-LL(mc)*dd<0) { t+=ans/dd; break; } } ans+=mc*d[ed]; t+=mc; for(x=ed;x!=st;x=pos[x]) { a[pre[x]].c-=mc; a[a[pre[x]].other].c+=mc; } } } int main() { int n; 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]); for(int i=1;i<=n;i++) f[i]=zys(A[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) if(A[i]%A[j]==0&&f[i]-f[j]==1){ins(i,j+n,999999999,c[i]*c[j]);ins(j,i+n,999999999,c[i]*c[j]);} st=0;ed=n+n+1; for(int i=1;i<=n;i++) { ins(st,i,B[i],0LL); ins(i+n,ed,B[i],0LL); } t=0;Flow(); printf("%lld\n",t/2LL); return 0; }