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;
}

 

posted @ 2018-03-05 21:02  Star_Feel  阅读(214)  评论(0编辑  收藏  举报