[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、……、bn。
第四行 n 个整数 c1、c2、……、cn。

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

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

Sample Output
4

HINT
\(n\leqslant 200,a_i\leqslant 10^9,b_i\leqslant 10^5,|c_i|\leqslant 10^5\)

首先我们对\(a_i\)进行质因数分解,有\(a_i=\prod\limits_{i=1}^mp_i^{k_i}\),记\(d_i=\sum\limits_{i=1}^mk_i\),那么两个数\(i,j\)的配对条件,当且仅当\(a_j|a_i\)\(d_i=d_j+1\)(\(a_i>a_j\)),于是我们对\(d_i\)的奇偶性进行分组,源点向所有\(d_i\)为奇数的点连一条流量为\(b_i\),费用为0的边;所有\(d_i\)为奇数的点向汇点连一条流量为\(b_i\),费用为0的边。

对于所有能匹配的\(i,j\),令\(d_i\)为奇数,则\(i\)\(j\)连一条流量为\(\infty\),费用为\(c_i\times c_j\)的边,然后跑最大费用最大流即可

由于要保证费用\(>0\)时流量最大,因此\(EK\)扩展的时候稍微有点不同,具体可以见代码

三年OI一场空,不开longlong见祖宗(我的1h啊)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1; char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=2e2,M=2.1e4;
int cnt[N+10],V[N+10],A[N+10];
int pre[M+10],now[N+10],child[M+10],val[M+10];
ll cost[M+10],dis[N+10],Maxcost;
int flow[N+10],Line[N+10],Frm[N+10];
bool vis[N+10];
int n,S,T,Maxflow,tot=1;
void join(int x,int y,int z,ll v){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z,cost[tot]=v;}
void insert(int x,int y,int z,ll v){join(x,y,z,v),join(y,x,0,-v);}
bool SPFA(){
    static int h[N+10];
    int head=0,tail=1;
//  memset(Frm,255,sizeof(Frm));
    memset(dis,128,sizeof(dis));
    memset(flow,127,sizeof(flow));
    h[1]=S,vis[S]=1,dis[S]=0,Frm[T]=-1;
    while (head!=tail){
        if (++head>N)   head=1;
        int Now=h[head];
        for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
            if (val[p]>0&&dis[son]<dis[Now]+cost[p]){
                dis[son]=dis[Now]+cost[p];
                Frm[son]=Now;
                Line[son]=p;
                flow[son]=min(flow[Now],val[p]);
                if (!vis[son]){
                    if (++tail>N)   tail=1;
                    vis[h[tail]=son]=1;
                }
            }
        }
        vis[Now]=0;
    }
    return Frm[T]!=-1;
}
void MCMF(){
    while (SPFA()){
        if (Maxcost+1ll*flow[T]*dis[T]<0){//注意直接退出
            Maxflow+=(Maxcost/-dis[T]);
            break;
        }
        Maxflow+=flow[T];
        Maxcost+=1ll*flow[T]*dis[T];
        int Now=T;
        while (Now!=S){
            val[Line[Now]  ]-=flow[T];
            val[Line[Now]^1]+=flow[T];
            Now=Frm[Now];
        }
    }
}
int main(){
    n=read(),S=n+1,T=S+1;
    for (int i=1;i<=n;i++){
        A[i]=read(); int x=A[i];
        for (int j=2;j*j<=x;j++)
            while (x%j==0)
                x/=j,cnt[i]++;
        if (x!=1)   cnt[i]++;
    }
    for (int i=1;i<=n;i++){
        int y=read();
        cnt[i]&1?insert(S,i,y,0):insert(i,T,y,0);
    }
    for (int i=1;i<=n;i++)  V[i]=read();
    for (int i=1;i<=n;i++)
        if (cnt[i]&1)
            for (int j=1;j<=n;j++)
                if (abs(cnt[i]-cnt[j])==1)
                    if (A[i]%A[j]==0||A[j]%A[i]==0)
                        insert(i,j,inf,1ll*V[i]*V[j]);
    MCMF();
    printf("%d\n",Maxflow);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/10252529.html