• 【BZOJ-3308】九月的咖啡店 最大费用最大流 + 线性筛素数


    3308: 九月的咖啡店

    Time Limit: 30 Sec  Memory Limit: 128 MB
    Submit: 159  Solved: 56
    [Submit][Status][Discuss]

    Description

    深绘里在九份开了一家咖啡让,如何调配咖啡民了她每天的头等大事我们假设她有N种原料,第i种原料编号为i,调配一杯咖啡则需要在这里若干种兑在一起。不过有些原料不能同时在一杯中,如果两个编号为i,j的原料,当且仅当i与j互质时,才能兑在同一杯中。现在想知道,如果用这N种原料来调同一杯咖啡,使用的原料编号之和最大可为多少。

    Input

    一个数字N

    Output

    如题

    Sample Input

    10

    Sample Output

    30

    HINT

    1<=N<=200000

    Source

    Solution

    从N的范围,难以直接看出是网络流,但仔细分析,还是可以想到的

    比较厉害的建图,首先需要两个结论

    1.一个数的质因子,最多有两个

    2.并且这两个质因子,一个小于$sqrt{n}$,一个大于$sqrt{n}$

    那么可以考虑筛出1~n的素数

    考虑建二分图

    源向小于$sqrt{n}$的质数连边,容量为1,费用为0

    大于$sqrt{n}$的质数向汇连边,容量为1,费用为0

    小于$sqrt{n}$的质数$a$向大于$sqrt{n}$的质数$b$连边,容量为1,费用为$V_{ab}-V_{a}-V_{b}$

    $V_{a}$表示单独选$a$的最大收益$V_{a}=a^{lgn/lga}$

    $V_{b}$表示单独选$b$的最大收益$V_{b}=b$

    $V_{ab}$表示同时选$a$和$b$的最大收益$V_{ab}=a^{lg(n/b)/lga}*b$

    这样效率并不是特别的高,那么还可以加上两个优化:

    1.如果一个质数>n/2,那么它只能单独选,不用连边

    2.如果一条边的费用<0,那么完全可以不连

    然后考虑 最大费用最大流 即可

    Code

    #include<iostream> 
    #include<cstdio> 
    #include<cmath> 
    #include<algorithm> 
    #include<cstring> 
    #include<queue> 
    using namespace std; 
    inline int read() 
    { 
        int x=0,f=1; char ch=getchar(); 
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; char ch=getchar();} 
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} 
        return x*f; 
    } 
    #define maxn 200010 
    #define maxm 200000*10+10 
    #define inf 0x7fffffff 
    int n,ans; 
    struct Edgenode{int next,to,from,cap,cost;}edge[maxm]; 
    int head[maxn],cnt=1; 
    //init 
    inline void add(int u,int v,int w,int c) 
    { 
        cnt++; edge[cnt].next=head[u]; head[u]=cnt; 
        edge[cnt].from=u; edge[cnt].to=v; edge[cnt].cap=w; edge[cnt].cost=c; 
    } 
    inline void insert(int u,int v,int w,int c) 
    {add(u,v,w,c); add(v,u,0,-c);} 
    //addedge 
    bool mark[maxn];int dis[maxn],S,T; 
    inline bool spfa() 
    { 
        queue<int>q; memset(mark,0,sizeof(mark)); 
        for (int i=S; i<=T; i++) dis[i]=-inf; 
        q.push(S); mark[S]=1; dis[S]=0; 
        while (!q.empty()) 
            { 
                int now=q.front(); q.pop(); 
                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; 
                            if (!mark[edge[i].to]) 
                                mark[edge[i].to]=1,q.push(edge[i].to); 
                        } 
                mark[now]=0; 
            } 
        return dis[T]>0; 
    } 
    inline int dfs(int loc,int low) 
    { 
        mark[loc]=1; 
        if (loc==T) return low; 
        int w,used=0; 
        for (int i=head[loc]; i; i=edge[i].next) 
            if (edge[i].cap && dis[edge[i].to]==dis[loc]+edge[i].cost && !mark[edge[i].to]) 
                { 
                    w=dfs(edge[i].to,min(low-used,edge[i].cap)); 
                    edge[i].cap-=w; edge[i^1].cap+=w; used+=w; 
                    ans+=w*edge[i].cost; if (used==low) return low; 
                } 
        return used; 
    } 
    inline void zkw() 
    { 
        int tmp=0; 
        while (spfa()) 
            { 
                mark[T]=1; 
                while (mark[T]) 
                    memset(mark,0,sizeof(mark)),tmp+=dfs(S,inf); 
            } 
    } 
    //MaxflowMaxcost 
    bool flag[maxn]; int prime[maxn],tot; 
    inline void prework(int maxx) 
    { 
        flag[1]=1; 
        for (int i=2; i<=maxx; i++) 
            { 
                if (!flag[i]) prime[++tot]=i; 
                for (int j=1; j<=tot && i*prime[j]<=maxx; j++) 
                    { 
                        flag[i*prime[j]]=1; 
                        if (!(i%prime[j])) break; 
                    } 
            } 
    } 
    //get prime 
    inline int calc(int n,int x) 
    { 
        long long t=x; 
        while (t*x<=n) t=t*x; 
        return t; 
    } 
    //calc 
    inline void make() 
    { 
        S=0,T=tot+1; int pos=0; 
        for (int i=1; i<=tot; i++) 
            { 
                if (prime[i]>=n/2) {ans+=prime[i];continue;} 
                if ((long long)prime[i]*prime[i]<=n) 
                    insert(S,i,1,0),ans+=calc(n,prime[i]); 
                else 
                    pos=(!pos)?i:pos,insert(i,T,1,0),ans+=prime[i]; 
            } 
        for (int i=1; i<pos; i++) 
            for (int j=pos; j<=tot; j++) 
                { 
                    if ((long long)prime[i]*prime[j]>n) break; 
                    int tmp=calc(n/prime[j],prime[i])*prime[j]-calc(n,prime[i])-prime[j];   
                    if (tmp>0) insert(i,j,1,tmp);   
                } 
    } 
    //build 
    int main() 
    { 
        n=read(); 
        prework(n); 
        make(); 
        zkw(); 
        printf("%d
    ",ans+1); 
        return 0; 
    }

    忽略桑心病狂的inline.. && 自己YY的最大费用最大流...好像还可以?

  • 相关阅读:
    initramfs扫描磁盘前改变磁盘上电顺序
    “井号键”用英语怎么说?
    syslog,rsyslog and syslog-ng
    glob (programming) and spool (/var/spool)
    CentOS 6.5语言包裁剪
    C​P​U​_​C​S​t​a​t​e​_​P​S​t​a​t​e and then ACPI on Wiki
    we are experimenting with a new init system and it is fun
    linux init->upstart->systemd
    微信浏览器内建的WeixinJSBridge 实现“返回”操作
    npm i node-sass 报错&npm 镜像切换
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5389347.html
Copyright © 2020-2023  润新知