zoukankan      html  css  js  c++  java
  • 【BZOJ-4514】数字配对 最大费用最大流 + 质因数分解 + 二分图 + 贪心 + 线性筛

    4514: [Sdoi2016]数字配对

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 726  Solved: 309
    [Submit][Status][Discuss]

    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

    Source

    鸣谢Menci上传

    Solution

    二分图建图方式,分析一下

    如果$a[i]$和$a[j]$互质,不妨把它们分解质因子,互质即质因子约去后的质因子互质,或者只剩一个质因子

    所以考虑先预处理 线性筛质数,再质因数分解,含有奇数个质因子的与含有偶数个质因子的分列两排

    如果满足$a[i]$和$a[j]$可连($a[i]$为质因数为奇的$a[j]$为质因数为偶的),则由$i--->j$连容量$inf$,费用$c[i]*c[j]$

    由$S--->odd[i]$连容量$b[odd[i]]$,费用为$0$;同理$even[i]--->T$连容量$b[even[i]]$费用$0$

    然后跑最大费用最大流,因为最大费用最大流每次增广的路径费用严格不下降,所以,贪心的一直跑到总费用$<=0$的时候,单独判断一下即可

    容易出问题的细节:

    1.注意开longlong...

    2.Inf的范围要注意(习惯性的开0x7fffffff导致WA5组...)

    3.不能暴力筛和暴力分解,时间复杂度不科学

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 1010
    #define maxm 100010
    int n,a[maxn],b[maxn];long long c[maxn]; bool f=0;
    struct EdgeNode{int next,cap,to,from;long long cost;}edge[maxm];
    int head[maxn],cnt=1;
    void add(int u,int v,int w,long long c)
    {
        cnt++;
        edge[cnt].to=v;edge[cnt].from=u;edge[cnt].cap=w;edge[cnt].cost=c;edge[cnt].next=head[u];head[u]=cnt;
    }
    void insert(int u,int v,int w,long long c) {add(u,v,w,c); add(v,u,0,-c);}
    int from[maxn],S,T; long long dis[maxn]; bool mark[maxn];
    #define inf 10000000000000LL
    bool spfa()
    {
        queue<int>q;
        for (int i=S; i<=T; i++) dis[i]=-inf; memset(from,0,sizeof(from));
        q.push(S); mark[S]=1; dis[S]=0;
        while (!q.empty())
            {
                int now=q.front(); q.pop(); mark[now]=0;
                for (int i=head[now]; i; i=edge[i].next)
                    if (edge[i].cap && dis[edge[i].to]<dis[now]+edge[i].cost)
                        {
                            dis[edge[i].to]=dis[now]+edge[i].cost; from[edge[i].to]=i;
                            if (!mark[edge[i].to])
                                q.push(edge[i].to),mark[edge[i].to]=1;
                        }
            } 
        return dis[T]!=-inf;
    }
    long long Cost; int Flow;
    void MaxCostFlow()
    {
        int flow=inf;
        for (int i=from[T]; i; i=from[edge[i].from])
            flow=min(flow,edge[i].cap);
        
        if ((long long)Cost+flow*dis[T]>=0LL)
            {
                for (int i=from[T]; i; i=from[edge[i].from])
                    edge[i].cap-=flow,edge[i^1].cap+=flow;
                Flow+=flow; Cost+=dis[T]*flow;
            
            }
        else 
            Flow+=abs(Cost/dis[T]),Cost-=(Cost/dis[T])*dis[T],f=1;
       // Flow+=flow;
        //printf("%d %I64d %I64d
    ",Flow,Cost,dis[T]);
    }
    int prime[35001],tot; bool flag[35001]; 
    void Prework()
    {
        flag[1]=1;
        for (int i=2; i<=32000; i++)
            {
                if (!flag[i]) prime[++tot]=i;
                for (int j=1; j<=tot&&i*prime[j]<=32000; j++)
                    flag[i*prime[j]]=1;
            }
    }
    int odd[maxn],even[maxn],ot,et;
    bool check(int x,int y)
    {
        if (x%y && y%x || !x || !y) return 0;
        int tmp=max(x/y,y/x);
        for (int i=1; i<=tot&&prime[i]<tmp; i++)
            if (tmp%prime[i]==0) return 0;
        return 1;
    }    
    void Build()
    {
        Prework();
        S=0,T=n+1;
        for (int i=1,ct=0; i<=n; i++,ct=0)
            {
                for (int j=1,tmp=a[i]; j<=tot; j++,tmp=a[i])
                    while (tmp%prime[j]==0) tmp/=prime[j],ct++;
                        //else if (prime[j]>tmp) break;
                if (ct&1) odd[++ot]=i; else even[++et]=i;
            }
        for (int i=1; i<=ot; i++)
            for (int j=1; j<=et; j++)
                if (check(a[odd[i]],a[even[j]]))
                    insert(odd[i],even[j],inf,c[odd[i]]*c[even[j]])/*,printf("%d-->%d:%d/%I64d
    ",odd[i],even[j],inf,c[odd[i]]*c[even[j]])*/;
        for (int i=1; i<=ot; i++) insert(S,odd[i],b[odd[i]],0LL)/*,printf("%d-->%d:%d/%I64d
    ",S,odd[i],b[odd[i]],0LL)*/;
        for (int i=1; i<=et; i++) insert(even[i],T,b[even[i]],0LL)/*,printf("%d-->%d:%d/%I64d
    ",even[i],T,b[even[i]],0LL)*/;
        //printf("%d %d %d
    ",ot,et,cnt);
    }
    int main()
    {
    //    freopen("menci_pair.in","r",stdin);
    //    freopen("menci_pair.out","w",stdout);
        n=read();
        for (int i=1; i<=n; i++) a[i]=read();
        for (int i=1; i<=n; i++) b[i]=read();
        for (int i=1; i<=n; i++) c[i]=(long long)read();
        Build();
        while (spfa() && !f) MaxCostFlow();
        printf("%d
    ",Flow);
        return 0;
    }

    当时唯一想出和出题人一模一样的解法的题,但是最后不知道莫名其妙的写错了什么地方,结果连暴力分都不到....(回来之后这么久才想起来重写一遍..)

  • 相关阅读:
    动态设置ImageView的大小
    URI和URL、URN的区别
    关于Android中的异步任务
    Android下如何理解onMeasure,onLayout的过程
    Android如何获取同包名应用的图标及名称
    Android4.0 监听 android 手机情景模式(有声、静音、振动)
    Android中裁剪一张图片
    Json解析国家气象局天气API
    Android中AnimationDrawable的使用
    POJ
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5476469.html
Copyright © 2011-2022 走看看