[bzoj4514][SDOI2016]数字配对

11 篇文章 0 订阅
10 篇文章 0 订阅

题目大意

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

二分图!

我们来考虑配对条件:
如果记f(x)表示x分解质因数后的项数(注意4=2*2所以f(4)=2,也就是说这里指的是分成若干项每一项均为质数的项数)
那么x和y可以配对的条件:f(x)=f(y)+1且x是y的倍数。
易证。
那么如果可以配对进行连边的。
我们把f值为奇数的放一边,f值为偶数的放一边,出来的是二分图!

网络流

源向i/i向汇连容量为bi费用为0的边。
i、j可以匹配,i向j连容量为无穷费用为ci*cj的边。
接下来,我们来描述任务:
分配流量使得每条边的流量不大于容量,且获得的费用和不小于0,求最大流。
我们来贪心!
每次沿着费用最长路增广。
如果当前源与汇不连通或者即使往最长路增广1的流量都会使总费用小于0就退出。
假设当前最长路为t,可以增广l的流量。
如果该增广路费用和为负数,那么需要注意保证总费用小于0,详见代码。
然后增广就是了。
(第一次用spfa写费用流

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<deque>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll maxn=200+10,maxm=50000+10,maxd=1000000000,maxdd=1000000,inf=100000000000000000;
ll h[maxn],go[maxm*2],dis[maxm*2],cost[maxm*2],fx[maxm*2],next[maxm*2];
ll a[maxn],b[maxn],c[maxn],col[maxn];
ll d[maxn],rl[maxn],pre[maxn];
deque<ll> dl;
ll pri[maxdd];
bool bz[maxdd];
ll i,j,k,l,r,s,t,n,m,tot,top,ans,now;
void add(ll x,ll y,ll z,ll c,ll d){
    go[++tot]=y;
    dis[tot]=z;
    cost[tot]=c;
    fx[tot]=tot+d;
    next[tot]=h[x];
    h[x]=tot;
}
void spfa(){
    fo(i,s,t) d[i]=-inf;
    fo(i,s,t) bz[i]=0;
    d[s]=0;
    rl[s]=inf;
    dl.push_back(s);
    bz[s]=1;
    while (!dl.empty()){
        j=dl.front();
        r=h[j];
        while (r){
            if (dis[r]&&d[j]+cost[r]>d[go[r]]){
                d[go[r]]=d[j]+cost[r];
                pre[go[r]]=r;
                rl[go[r]]=min(rl[j],dis[r]);
                if (!bz[go[r]]){
                    bz[go[r]]=1;
                    dl.push_back(go[r]);
                }
            }
            r=next[r];
        }
        dl.pop_front();
        bz[j]=0;
    }
}
int main(){
    freopen("pair.in","r",stdin);freopen("pair.out","w",stdout);
    fo(i,2,floor(sqrt(maxd))){
        if (!bz[i]) pri[++top]=i;
        fo(j,1,top){
            if (i*pri[j]>floor(sqrt(maxd))) break;
            bz[i*pri[j]]=1;
            if (i%pri[j]==0) break;
        }
    }
    scanf("%lld",&n);
    fo(i,1,n){
        scanf("%lld",&a[i]);
        k=a[i];
        t=0;
        fo(j,1,top)
            while (k%pri[j]==0){
                t++;
                k/=pri[j];
            }
        col[i]=t;
    }
    fo(i,1,n) scanf("%lld",&b[i]);
    fo(i,1,n) scanf("%lld",&c[i]);
    fo(i,1,n)
        if (col[i]%2)
            fo(j,1,n)
                if (col[j]==col[i]-1&&a[i]%a[j]==0||col[j]==col[i]+1&&a[j]%a[i]==0){
                    add(i+1,j+1,inf,c[i]*c[j],1);
                    add(j+1,i+1,0,-c[i]*c[j],-1);
                }
    s=1;t=n+2;
    fo(i,1,n){
        if (col[i]%2){
            add(s,i+1,b[i],0,1);
            add(i+1,s,0,0,-1);
        }
        else{
            add(i+1,t,b[i],0,1);
            add(t,i+1,0,0,-1);
        }
    }
    while (1){
        spfa();
        if (d[t]==-inf||now+d[t]<0) break;
        if (d[t]>=0) l=rl[t];else l=min(rl[t],now/(-d[t]));
        ans+=l;
        now+=d[t]*l;
        j=t;
        while (j!=s){
            dis[pre[j]]-=l;
            dis[fx[pre[j]]]+=l;
            j=go[fx[pre[j]]];
        }
    }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值