【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\n",ans+1); 
    return 0; 
}

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

posted @ 2016-04-13 23:35  DaD3zZ  阅读(469)  评论(0编辑  收藏  举报