BZOJ 4514 浅谈最大费用最大流

BZOJ 4514 浅谈最大费用最大流_第1张图片
世界真的很大
网络流并不难,但是其建图方式的多种多样,决定了其能解决的问题的多样性。
而作为网络流的一个变种,在其基础之上加上边权的费用流,除了一眼就能看出的模板题以外,取决于建模方式的灵活变化也很多
而对于这样变化多端的题而言,我们往往很难一眼看出来这道题究竟该不该用网络流或者费用流
我们寻求题解,但却不能仅仅止步于AC,不应该忘记去推导建模的过程
在感叹“哇这道题真是巧”的同时,还是得去想想到底为什么能这么想,或者大佬是怎么把这道题想出来的,“怎么”比“什么”更重要
尽管的却很难
网络流费用流一类正是如此
看题先:

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

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

看题目,问最多的配对次数
先考虑配对条件,理解一下就是说ai等于aj乘上一个质数
由除法,质数什么的可以联想到质因数什么的,就是说ai质因数分解后质因数比aj多一个,而且恰恰多一个
那么对于所有可以匹配的ai,aj,其质因数个数一定是一个奇数一个偶数,不然没法差1对吧
而且我们可以看出,对于质因数个数同为奇数或者偶数的两个数,那肯定是不可能配对的
很明显的可以把图分成一张二分图了,只有在图的两侧的点能互相连接配对
处理二分图的一般就是匈牙利或者网络流了
继续考虑到底是把数分成二分图,还是把数的种类分成二分图
这里就对题目数据范围逆向分析了
种类的范围是200,数的范围是200*10^5就是10^7级别,不管是对于匈牙利还是网络流都太大了一点,相对来说还是200更为合适
那么就按照种类,把a值质因数个数为奇数的排成一列,偶数的排成一列,相互能配对的互相建边
但是如果不按数的个数来的话,b就没有作用了,换句话说,就需要一个方法来限制每种数的数量,很显然,匈牙利貌似没有这个功能
那么就是网络流了,dinic一类的东西
源点想所有奇数点连流量为b值的边,所有偶数点向汇点连流量为b值的边
确立大概算法之后就带着算法的印象来思考
题目要求的是在权值和大小不低于0的情况下考虑最多的匹配次数
考虑本来已经是按照匹配连边的,所以从源点到汇点的一条最大流就是最大流次匹配。
而我们现在要考虑的是最大流的匹配次数带来的权值,就是匹配次数乘以ci*cj
就是最大流*ci*cj
把ci*cj看成权值的话,妥妥的费用流了
费用流有一个找增广路的过程,我们需要的就是找了多少次增广路
那么为了使得增广路尽量多,那么我们就需要使得权值尽量大,这样就离0尽量远
这样就得出:我们找增广路时需要的是权值的最长路而不是最短路
需要特判一下最后跑不完增广路的最大流的情况
费用流就用一般的多遍SPFA的办法
强行模拟增广路的过程,每次都找最长路,这样最后就是最大费用最长路了
单独关于费用流的之后再总结今天就现总结一下解题思路吧
完整代码:

#include
#include
#include
#include
using namespace std;
typedef long long dnt;

const dnt INF=100000000000000000ll ;

struct edge
{
    dnt u,v,last,c,w;
}ed[100010];

queue  state ;

dnt ans=0,now=0,a[100010],b[100010],c[100010],dis[100010];
dnt n,num=1,ptot=0,S,T;
dnt head[100010],frm[100010],se[100010];
dnt isnot[100010],primes[100010],cnt[100010];

void add(dnt u,dnt v,dnt w,dnt c)
{
    num++;
    ed[num].u=u;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].c=c;
    ed[num].last=head[u];
    head[u]=num;
}

void init(dnt N)
{
    isnot[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!isnot[i])
            primes[ptot++]=i;
        for(int t=0;tif(j>N) break ;
            isnot[j]=1;
            if(i%primes[t]==0)
                break ;
        }
    }
}

void check(dnt x)
{
    dnt tmp=a[x],t=0;
    for(int i=0;iif(tmp%primes[i]==0)
        {
            while(tmp%primes[i]==0)
            {
                tmp/=primes[i];

                cnt[x]++;
            }
        }
    }
}

bool judge(dnt x,dnt y)
{
    if(cnt[x]==cnt[y]+1 && a[x]%a[y]==0) return true ;
    if(cnt[y]==cnt[x]+1 && a[y]%a[x]==0) return true ;
    return false ;
}


bool SPFA()
{
    for(dnt i=0;i<=T;i++) dis[i]=-INF;
    memset(se,0,sizeof(se));
    while(!state.empty()) state.pop();
    se[S]=1,dis[S]=0;
    state.push(S);
    while(!state.empty())
    {
        int u=state.front();
        se[u]=0;
        state.pop();
        for(int i=head[u];i;i=ed[i].last)
        {
            dnt v=ed[i].v;
            if(ed[i].w && dis[v]if(!se[v])
                {
                    state.push(v);
                    se[v]=1;
                }
            }
        }
    }
    if(now+dis[T]<0) return false ;
    return dis[T]!=-INF;
}

void MCF()
{
    dnt x=INF,i=frm[T];
    while(i)
    {
        x=min(x,ed[i].w);
        i=frm[ed[i].u];
    }
    if(now+dis[T]*x<0)
    {
        ans+=now/(-dis[T]);
        for(dnt i=head[S];i;i=ed[i].last) ed[i].w=0;
        return ;
    }
    i=frm[T];
    now+=x*dis[T];
    while(i)
    {   
        ed[i].w-=x;
        ed[i^1].w+=x;
        i=frm[ed[i].u];
    }
    ans+=x;
}

int main()
{
    init(32003);
    scanf("%lld",&n);
    S=0,T=n+1;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%lld",&b[i]);
    for(int i=1;i<=n;i++)
        scanf("%lld",&c[i]);
    for(int i=1;i<=n;i++)
    {
        check(i);
        if(cnt[i]%2)
            add(S,i,b[i],0),add(i,S,0,0);
        else
            add(i,T,b[i],0),add(T,i,0,0);       
    }
    for(int i=1;i<=n;i++)
        if(cnt[i]%2)
            for(int j=1;j<=n;j++)
                if(judge(i,j))
                    add(i,j,INF,c[i]*c[j]),add(j,i,0,-c[i]*c[j]);           
    while(SPFA())
        MCF();
    SPFA();
    printf("%lld\n",ans);
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

你可能感兴趣的:(最短路,网络流,费用流)