P4068 [SDOI2016] 数字配对

P4068 [SDOI2016] 数字配对

题目描述

\(n\) 种数字,第 \(i\) 种数字是 \(a_i\)、有 \(b_i\) 个,权值是 \(c_i\)

若两个数字 \(a_i\)\(a_j\) 满足,\(a_i\)\(a_j\) 的倍数,且 \(a_i/a_j\) 是一个质数,

那么这两个数字可以配对,并获得 \(c_i \times c_j\) 的价值。

一个数字只能参与一次配对,可以不参与配对。

在获得的价值总和不小于 \(0\) 的前提下,求最多进行多少次配对。

输入格式

第一行一个整数 \(n\)

第二行 \(n\) 个整数 \(a_1,a_2,\cdots,a_n\)

第三行 \(n\) 个整数 \(b_1,b_2,\cdots,b_n\)

第四行 \(n\) 个整数 \(c_1,c_2,\cdots,c_n\)

输出格式

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

说明/提示

\(6 \sim 10\): $n \leq 200 $, $a_i \leq 10 ^ 9 $ , \(b_i \leq 10 ^ 5\) ,$ | c_i | \leq 10 ^ 5$。

Solution:

我们先打一个 \([1,10^5]\) 的质数表,然后我们将每个 \(a_i\) 分解质因数。那么题目中的条件 "若两个数字 \(a_i\)\(a_j\) 满足,\(a_i\)\(a_j\) 的倍数,且 \(a_i/a_j\) 是一个质数" 的前半段是很好判定的,于是我们可以将后半段转化为 \(a_i,a_j\) 的质因数分解中有且仅有一位不同,且不同的那位的指数差值为 1 .

然后我们又发现 "\(a_i\)\(a_j\) 的倍数" 这启示我们 \(\frac{a_i}{a_j}\) 这个数的质因数分解中有且仅有一位是 \(1\) 剩下的都是 \(0\),并且这个条件是充要的.所以我们可以直接统计 \(a_i\) 的质因数分解中为一位上指数的和 $ sum_i $.

形式化的:

\(a_i= \sum {P_j^{k_j}}\ \ ,\ \ sum_i=\sum k_j\)

那么我们的条件就等价于 \(sum_i-sum_j=1\)

然后我们就将这题的条件刻画出来了,不论是观察数据范围还是仔细思考一下都能快速联想到费用流。所以我们将其连边,对每个点 \(i\) 的连边即为:

\((S,i,b_i,0)\)
\((i,j,b_i,a_i\times a_j)\)
\((i,T,b_i,0)\)

边的四元组的意义是:
(起点,终点,流量,花费)

然后我们每次增广都希望将权值(费用)最大化,同时总权值(费用)不得小于0,需要注意一些细节。

然后这题就做完了。

Code:

(年幼时写的 \(O(n\sqrt{n})\) 暴力质数筛,但是懒得改了)

反正数据这么水

#include<bits/stdc++.h>
const long long N=50005;
const long long inf=1e17;
using namespace std;
long long n,m,zs_cnt,e_cnt=1,ans,sum;
long long zs[10005],head[N],dis[N],zs_num[N];
long long a[N],b[N],c[N],dl[N],flow[N],pre[N];
struct Edge{
    long long to,nxt,flow,w;
}e[N<<2];
void add(long long x,long long y,long long flow,long long w)
{
    e[++e_cnt]={y,head[x],flow,w};
    head[x]=e_cnt;
}
bool check(long long x)
{
    for(long long i=3;i<=sqrt(x);i+=2)
    {
        if(x%i==0)return 0;
    }
    return 1;
}
void zs_init()
{
    long long idx=1e5;
    zs[++zs_cnt]=2;
    for(long long i=3;i<=idx;i+=2)
    {
        if(check(i))
        {
            zs[++zs_cnt]=i;
        }
    }
}
void change(long long x,long long id)
{
    long long now=1;
    while(x>1&&now<=zs_cnt)
    {
        if(x%zs[now]==0)
        {
            zs_num[id]++;
            x/=zs[now];
        }
        else
        {
            now++;
        }
    }
    if(now==zs_cnt+1&&x)
    {
        zs_num[id]=1;
        return ;
    }
    return ;
}
void init()
{
    for(long long i=0;i<=n+10;i++)
    {
        flow[i]=dl[i]=0;dis[i]=-inf;pre[i]=-1;
    }
}
queue<long long> Q;
long long bj=0;
bool spfa(long long s,long long t)
{
    init();
    dis[s]=0;dl[s]=1;flow[s]=inf;
    Q.push(s);
    while(!Q.empty())
    {
        long long u=Q.front();dl[u]=0;Q.pop();
        for(long long i=head[u];i;i=e[i].nxt)
        {
            long long v=e[i].to,w=e[i].w;
            if((dis[v]<dis[u]+w)&&(e[i].flow>0))
            {
                dis[v]=dis[u]+w;pre[v]=i;
                flow[v]=min(flow[u],e[i].flow);
                if(!dl[v]){Q.push(v);dl[v]=1;}
            }
        }
    }
    return ~pre[n+1];
}
void solve()
{
    long long ans=0,fl,now;
    while(spfa(0,n+1))
    {
        if(ans+dis[n+1]<0)break;fl=flow[n+1];
        if(dis[n+1]<0){fl=min(fl,ans/(-dis[n+1])); }
        ans+=fl*dis[n+1],sum+=fl;now=n+1;
        while(now!=0)
        {
            long long id=pre[now];long long u=e[id^1].to;
            e[id].flow-=fl;e[id^1].flow+=fl;now=u;
        }
    }
    printf("%lld",sum);
}
void work()
{
    zs_init();
    cin>>n;
    for(long long i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        change(a[i],i);
    }
    for(long long i=1;i<=n;i++)
    {
        scanf("%lld",&b[i]);
        if(zs_num[i]&1)add(0,i,b[i],0),add(i,0,b[i],0);
        else add(i,n+1,b[i],0),add(n+1,i,b[i],0);
    }
    for(long long i=1;i<=n;i++)
    {
        scanf("%lld",&c[i]);
    }
    for(long long i=1;i<=n;i++)
    {
        for(long long j=1;j<=n;j++)
        {
            if((a[i]%a[j]==0)&&abs((zs_num[i]-zs_num[j])==1))
            {
                long long x=i,y=j;
                if(zs_num[j]&1)swap(x,y);
                add(x,y,b[x],c[x]*c[y]);
                add(y,x,0,-c[x]*c[y]);
            }
        }
    }
    solve();
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("num.out","w",stdout);
    work();
    return 0;
}
posted @ 2025-05-08 21:26  liuboom  阅读(25)  评论(0)    收藏  举报