BZOJ4514:[SDOI2016]数字配对——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4514

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

参考洛谷题解。

这题很明显是要网络流的,且将关系图建出来之后很明显是二分图。

那么我们自然的将点归为两类,一类连S,一类连T,这样我们的边就有了方向性,然后就可以跑最大费用最大流了!

但是难受的是,我们跑的费用要求始终不低于0,这就很难办,我们简单的费用流无法胜任这个工作。

但是可以发现的是,我们spfa找可行流的时候,显然是先走大费用路径再走小费用路径,于是我们在spfa上直接跑网络流,一边更新dis,一边记录当前节点流入了多少,显然每次只能跑出来一条路径一个流因此会比原版费用流慢很多,复杂度O(玄学)反正能过。

以及我们当然不必要将二分图建出来dfs,我们记录tot[i]表示第i个数的质因子个数,则奇数为一堆偶数为一堆即可。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF=1e9;
const ll LINF=1e18;
const int N=210;
const int M=N*N*4;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int to,nxt,w;
    ll b;
}e[M];
int n,head[N],cnt,a[N],b[N],c[N],tot[N],limit[N],pre[N];
ll dis[N];
bool vis[N];
inline void add(int u,int v,int w,ll b){
    e[++cnt].to=v;e[cnt].w=w;e[cnt].b=b;e[cnt].nxt=head[u];head[u]=cnt;
    e[++cnt].to=u;e[cnt].w=0;e[cnt].b=-b;e[cnt].nxt=head[v];head[v]=cnt;
}
int suan(int k){
    int ans=0;
    for(int i=2;i*i<=k;i++){
    while(k%i==0){
        ans++;k/=i;
    }
    }
    if(k!=1)ans++;
    return ans;
}
inline bool spfa(int s,int t,int m){
    queue<int>q;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for(int i=1;i<=m;i++)dis[i]=-LINF;
    limit[s]=INF;dis[s]=0;q.push(s);vis[s]=1;
    while(!q.empty()){
    int u=q.front();q.pop();vis[u]=0;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;ll b=e[i].b;
        if(e[i].w&&dis[v]<dis[u]+b){
        dis[v]=dis[u]+b;
        limit[v]=min(limit[u],e[i].w);
        pre[v]=i;
        if(!vis[v])
            vis[v]=1,q.push(v);
        }
    }
    }
    return dis[t]>-LINF;
}
inline int costflow(int S,int T,int m){
    int flow=0,delta;ll ans=0;
    while(spfa(S,T,m)){
    if(ans+dis[T]<0)break;
    if(dis[T]>=0)delta=limit[T];
    else delta=min((ll)limit[T],ans/(-dis[T]));
    ans+=dis[T]*delta;flow+=delta;
    for(int u=T;pre[u]!=-1;u=e[pre[u]^1].to){
        e[pre[u]].w-=delta;e[pre[u]^1].w+=delta;
    }
    }
    return flow;
}
int main(){
    memset(head,-1,sizeof(head));cnt=-1;
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)b[i]=read();
    for(int i=1;i<=n;i++)c[i]=read();
    for(int i=1;i<=n;i++)tot[i]=suan(a[i]);
    int S=n+1,T=S+1;
    for(int i=1;i<=n;i++){
    if(tot[i]&1)add(S,i,b[i],0);
    else add(i,T,b[i],0);
    if(tot[i]&1){
        for(int j=1;j<=n;j++){
        if((a[j]%a[i]==0&&tot[i]+1==tot[j])||
           (a[i]%a[j]==0&&tot[j]+1==tot[i]))
            add(i,j,INF,(ll)c[i]*c[j]);
        }
    }
    }
    printf("%d\n",costflow(S,T,T));
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-06-13 20:07  luyouqi233  阅读(232)  评论(0编辑  收藏  举报