bzoj4514 [Sdoi2016]数字配对

(http://www.elijahqi.win/2017/12/28/bzoj4514-sdoi2016%E6%95%B0%E5%AD%97%E9%85%8D%E5%AF%B9/)
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

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

Sample Input
3
2 4 8
2 200 7
-1 -2 1
Sample Output
4
HINT

n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5

这题 还是一个 要是看出 以分解质因数之后质因数的个数的奇偶性来划分的话 就是板子题了

可惜我看不出qwq

确实 因为 我只有一种可能就是我质因数个数 奇数和偶数才可以构成答案 所以不妨我把质因数个数位奇数的放在左边 偶数的放在右边 然后每个点 连容量位b[i]的点去源或者汇 然后如果这两个数 满足条件的话 则连上他们中间的c数组相乘作为费用的边 跑最大费用流即可 注意判断是否是质数的时候我一开始以为我既然都求出了not_prime这个数组了 那就直接用吧 不行,显然是错的啊 我有可能数超过了1e5 就gg了 所以还是循环一下 看能否分解质因数判断是否是质数 注意本题long long十分重要


#include<queue> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 440000
#define N 22000
#define inf 1LL<<60
#define ll long long
using namespace std; 
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
int tot,prime[110000],q1[220],q2[220],tot1,tot2,T,h[220],flag[220];ll f[220];
bool not_prime[110000];int path[220],pre[220],num=1,n,a[220],b[220],c[220];
struct node{
    int x,y,z,next;ll c;
}data[55000];
inline void insert1(int x,int y,int z,ll c){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;data[num].c=c;data[num].x=x;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;data[num].c=-c;data[num].x=y;
}
inline bool spfa(){
    for (int i=0;i<=n+1;++i) f[i]=-inf;
    memset(flag,0,sizeof(flag));queue<int>q;q.push(0);memset(pre,-1,sizeof(pre));f[0]=0;flag[0]=1;
    while(!q.empty()){
        int x=q.front();q.pop();flag[x]=0;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;ll c=data[i].c;
            if (f[x]+c>f[y]&&z){
                f[y]=f[x]+c;pre[y]=x;path[y]=i;
                if (!flag[y]) flag[y]=1,q.push(y);
            } 
        } 
    }if (pre[T]==-1) return 0;else return 1;
}
inline bool judge(int i,int j){
    if (!a[i]||!a[j]||a[i]%a[j]&&a[j]%a[i]) return 0;
    int dis=max(a[j]/a[i],a[i]/a[j]);
    for (int i=1;i<=tot;++i)
    if (prime[i]>=dis) break;else if (dis%prime[i]==0)return 0;return 1;
}
int main(){
    freopen("bzoj4514.in","r",stdin);
    n=read();for (int i=1;i<=n;++i) a[i]=read();T=n+1;not_prime[0]=1;not_prime[1]=1;
    for (int i=1;i<=n;++i) b[i]=read();for (int i=1;i<=n;++i) c[i]=read();
    for (int i=2;i<=1e5;++i){
        if (!not_prime[i]) prime[++tot]=i;
        for (int j=1;prime[j]*i<=1e5;++j) {
            not_prime[prime[j]*i]=1;if (i%prime[j]==0) break;
        }
    }
    for (int i=1;i<=n;++i){
        int sum=0,now=a[i];
        for (int j=1;j<=tot;++j){
            while(now%prime[j]==0) sum++,now/=prime[j];if (now==1) break;
        }
        if (sum%2) q1[++tot1]=i;else q2[++tot2]=i;
    }
    for (int i=1;i<=tot1;++i)
        for (int j=1;j<=tot2;++j)
            if (judge(q1[i],q2[j])) insert1(q1[i],q2[j],0x3f3f3f3f,(ll)c[q1[i]]*c[q2[j]]);
    for (int i=1;i<=tot1;++i) insert1(0,q1[i],b[q1[i]],0);ll ans1=0;
    for (int i=1;i<=tot2;++i) insert1(q2[i],T,b[q2[i]],0);int ans=0;
    //for (int i=2;i<=num;++i) printf("%d %d %d %d\n",data[i].x,data[i].y,data[i].z,data[i].c);
    while(spfa()){
        int minn=0x3f3f3f3f,now=T;ll tmp=0;
        while(now) minn=min(minn,data[path[now]].z),now=pre[now];now=T;
        while(now) {tmp+=data[path[now]].c;data[path[now]].z-=minn;data[path[now]^1].z+=minn;now=pre[now];}
        if (ans1+tmp*minn<0){
            ans-=ans1/tmp;break;
        }else ans+=minn,ans1+=tmp*minn;
    } printf("%d",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值