【BZOJ-3308】九月的咖啡店 最大费用最大流 + 线性筛素数
3308: 九月的咖啡店
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 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的最大费用最大流...好像还可以?
——It's a lonely path. Don't make it any lonelier than it has to be.