[SDOI2016][bzoj4514] 数字配对 [费用流]

题面

传送门

思路

一个数字能且只能匹配一次

这引导我们思考:一次代表什么?代表用到一定上限(b数组)就不能再用,同时每用一次会产生价值(c数组)

上限?价值?网络流!

把一次匹配设为一点流量,那产生的价值不就是费用了吗?

我们考虑把一种数字抽象成一个点,可以匹配的数字之间连边,费用为c[i]*c[j],流量上限为.....

等等,流量上限怎么设?

而且还有一个问题:这里的匹配是双向的,虽然可以$O\left(n^2\right)$求出所有匹配对,但是网络流要求是单向边啊!

别急,我们先来分析一下两个满足匹配条件的数,有什么性质

设$i=p\ast j$,其中p是一个质数

那我们考虑$i$和$j$的质因数分解,会发现:它们俩分解出的质因数个数之间正好差一!

这说明了什么?

这说明匹配只有可能在质因数个数奇偶性不同的数对之间存在,而如果根据质因数个数的奇偶性把数分成两组,那么所有边都在两组之间!

这是什么?二分图啊!

那么我们就可轻易把每条边定向成从奇数侧到偶数侧了!

接下来的事就简单了:我们建立超级源S和超级汇T,从S连边到所有质因数个数为奇数的点i,费用为0,容量为b[i],质因数个数为偶数的点连到T,类似

这样,我们也一同限制了每个点最多流出去不超过b[i]的流量,也就是不发生超过b[i]次和这个数字有关的匹配

因此对于原图中的可行匹配,只要连边,费用为c[i]*c[j],流量上限inf

跑最大费用最大流......等等好像不行?这道题是要求费用非负时的最大流量啊......

别急,我们来贪心一波

我们每次在图中做一个spfa,找到费用最大(最长)的增广路,设它的总长度(费用)为maxn,同时设当前总费用为ans

如果maxn<-ans,那么即使加上1的流量,总费用也负数了,这个时候结束循环,输出总流量flow即可

否则,如果maxn>0,那么非常高兴,我们随便加,流量越多越好

如果maxn<0,那么也没有问题,我们只要令流的流量为$min(limit,ans/(-maxn))$,其中limit为当前增广路的流量上限

这样一直循环,直到因为上面的原因跳出,或者图不连通了为止,输出总流量flow,就是最大匹配数了

贪心的证明很显然,我们每次都是取最优走,而且后面的决策肯定没有我优,就证完了

Code:

写的时候注意细节啊......这题细节贼多,一不小心就除0或者mod0了,而且实现分解质因数的时候注意,如果一个数x到了sqrt(x)都还没有一个质因数,那么它肯定是个质数

因此我们只要筛1e5的素数就够了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define inf 1e15
#define ll long long
using namespace std;
inline ll read(){
    ll re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
ll cnt=-1,ans,flow,first[210],dis[210],vis[210],limit[210],pre[210];
struct edge{
    ll to,next,w,cap;
}a[100010];
inline void add(ll u,ll v,ll w,ll cap){
    a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
    a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
bool spfa(ll s,ll t){
    ll q[5010]={0},head=0,tail=1,u,v,w,i;
    for(i=s;i<=t;i++) dis[i]=-inf;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));memset(limit,0,sizeof(limit));
    q[0]=s;vis[s]=1;dis[s]=0;limit[s]=inf;
    while(head<tail){
        u=q[head++];vis[u]=0;
        for(i=first[u];~i;i=a[i].next){
            v=a[i].to;w=a[i].w;
            if(a[i].cap&&(dis[v]<dis[u]+w)){//注意是最长路
                dis[v]=dis[u]+w;
                limit[v]=min(limit[u],a[i].cap);
                pre[v]=i;
                if(!vis[v]) q[tail++]=v,vis[v]=1;
            }
        }
    }
    return dis[t]!=-inf;
}
ll n,A[210],B[210],C[210],col[210];
ll tot=0,pri[100010],v[100010]={0};
void init(){//线筛
    ll i,j,k;v[1]=1;
    for(i=2;i<=100000;i++){
        if(!v[i]) pri[++tot]=i;
        for(j=1;j<=tot;j++){
            k=i*pri[j];if(k>100000) break;
            v[k]=1;
            if((i%pri[j])==0) break;
        }
    }
}
ll cntprime(ll x){
    ll re=0,c=1;
    while(x>1&&c<=tot){
        while((x%pri[c])==0) x/=pri[c],re++;
        c++;
    }
    if((c==tot+1)&&(x>1)) return 1;//处理特殊情况
    return re;
}
int main(){
    memset(first,-1,sizeof(first));
    ll i,j;init();
    n=read();
    for(i=1;i<=n;i++) A[i]=read(),col[i]=cntprime(A[i]);
    for(i=1;i<=n;i++){
        B[i]=read();
        if(col[i]%2) add(0,i,0,B[i]);
        else add(i,n+1,0,B[i]);
    }
    for(i=1;i<=n;i++) C[i]=read();
    for(i=1;i<=n;i++){
        for(j=i+1;j<=n;j++){
            if((((A[i]%A[j])==0)&&(col[i]==col[j]+1))||(((A[j]%A[i])==0)&&(col[j]==col[i]+1))){
                if(col[i]%2) add(i,j,C[i]*C[j],inf);
                else add(j,i,C[i]*C[j],inf);
            }
        }
    }
    ll tmp,u;
    while(1){
        if(!spfa(0,n+1)) break;
        if(dis[n+1]+ans<0) break;
        if(dis[n+1]>=0) tmp=limit[n+1];//注意这里>=不要写成>......我被这个坑了1h啊啊啊啊
        else tmp=min(limit[n+1],ans/(-dis[n+1]));
        ans+=dis[n+1]*tmp;flow+=tmp;
        for(u=n+1;~pre[u];u=a[pre[u]^1].to){
            a[pre[u]].cap-=tmp;a[pre[u]^1].cap+=tmp;
        }
    }
    printf("%lld\n",flow);
}

原文地址:https://www.cnblogs.com/dedicatus545/p/8729451.html

时间: 2024-10-11 13:14:53

[SDOI2016][bzoj4514] 数字配对 [费用流]的相关文章

【BZOJ4514】【SDOI2016】数字配对 [费用流]

数字配对 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. Input 第一行一

Luogu P4068 [SDOI2016]数字配对(费用流)

Luogu P4068 [SDOI2016]数字配对(费用流) 根据质因子个数奇偶性划分肯定会形成一张二分图. 把所有的\(a\)分解质因数,记录其质因子个数. \(a_i \% a_j == 0\)且\(a_i\)的质因子比\(a_j\)质因子个数多1的时候,我们连边. 解决这个题目的关键是求出费用\(>0\)的时候的最大的流量. 我们要跑最大费用最大流,(具体实现是把边权取反) 这样在每一次的增广过程中,我们都可以保证费用最大且满足流最多. 但是写法有异议,待填坑. 原文地址:https:/

【BZOJ4514】[Sdoi2016]数字配对 费用流

[BZOJ4514][Sdoi2016]数字配对 Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. Input 第一行一个整数 n. 第二行 n 个整数 a1.a2.…….an. 第三行 n 个整数 b1.b2.

BZOJ4514: [Sdoi2016]数字配对(费用流)

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2197  Solved: 859[Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最

【BZOJ 4514】[Sdoi2016]数字配对 费用流

利用spfa流的性质,我直接拆两半,正解分奇偶(妙),而且判断是否整除且质数我用的是暴力根号,整洁判断质数个数差一(其他非spfa流怎么做?) #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; const int N=205; const int P=N; const int E=N*N/2; const int Inf=0x3f3f3f3f; const LL

[SDOI2016 Round1] 数字配对

COGS 2221. [SDOI2016 Round1] 数字配对 http://www.cogs.pro/cogs/problem/problem.php?pid=2221 ★★★   输入文件:menci_pair.in   输出文件:menci_pair.out   简单对比时间限制:1 s   内存限制:128 MB [题目描述] 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 aiaj 是一个质数,那么这两

bzoj4514【SDOI2016】数字配对

4514: [Sdoi2016]数字配对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 730  Solved: 311 [Submit][Status][Discuss] Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对.

bzoj4514 数字配对

思路 首先想到费用流. 对于每个点拆点.然后考虑我们怎样才能保证每个点只被用一次. 如果\(i\)与\(j\)满足条件.那么就从\(i\)向\(j\)连一条边并且从\(j\)向\(i\)连一条边.这样每次增广的时候我们都可以看作某一条边被增广了两次.显然从\(i\)到\(j\)和从\(j\)到\(i\)的边是等价的.也就是说,如果当前增广这两个点之间的边更优秀,那么在增广完成从\(i\)到\(j\)和从\(j\)到\(i\)这两条边流量变为\(0\)之前不回去增广其他的边. 比较难解释,仔细想一

cogs 2221. [SDOI2016 Round1] 数字配对

★★ 输入文件:pair.in 输出文件:pair.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,那么这两个数字可以配对,并获得 ci×cj 的价值. 一个数字只能参与一次配对,可以不参与配对. 在获得的价值总和不小于 0 的前提下,求最多进行多少次配对. [输入格式] 第一行一个整数 n. 第二行 n 个整数 a1.