Description
深绘里在九份开了一家咖啡让,如何调配咖啡民了她每天的头等大事
我们假设她有N种原料,第i种原料编号为i,调配一杯咖啡则需要在这
里若干种兑在一起。不过有些原料不能同时在一杯中,如果两个编号
为i,j的原料,当且仅当i与j互质时,才能兑在同一杯中。
现在想知道,如果用这N种原料来调同一杯咖啡,使用的原料编号之和
最大可为多少。
1<=N<=200000
Solution
首先这道题有两个结论
结论一:所有被选的数字最多只有两个质因数
结论二:所有有两个质因数的被选数字一个质因数大于√n另一个质因数小于√n
知道了就很好做了。根据与√n的大小关系黑白染色然后连边跑最大费用最大流即可
一个跑得比较快的方法是先把所有单独质数的答案加上,对于一条连接(p[i],p[j])的边考虑它代表的含义,连容量为1费用为v[a*b]-v[a]-v[b],这里v(a)表示质数a的贡献
Code
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
typedef long long LL;
const int INF=1000000007;
const int N=400005;
const int E=400005;
struct edge {int x,y,w,c,next;} e[E];
int prime[N],n;
int dis[N],pre[N];
int ls[N],edCnt=1;
bool not_prime[N],vis[N];
std:: queue <int> que;
void add_edge(int x,int y,int w,int c) {
e[++edCnt]=(edge) {x,y,w,c,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {y,x,0,-c,ls[y]}; ls[y]=edCnt;
}
void pre_work(int n) {
rep(i,2,n) {
if (!not_prime[i]) prime[++prime[0]]=i;
for (int j=1;i*prime[j]<=n&&j<=prime[0];j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
bool spfa(int st,int ed) {
fill(dis,-31); int inf=dis[st]; dis[st]=0;
que.push(st); vis[st]=1;
while (!que.empty()) {
int now=que.front(); que.pop();
for (int i=ls[now];i;i=e[i].next) {
if (e[i].w>0&&dis[now]+e[i].c>dis[e[i].y]) {
dis[e[i].y]=dis[now]+e[i].c;
pre[e[i].y]=i;
if (!vis[e[i].y]) {
vis[e[i].y]=1;
que.push(e[i].y);
}
}
}
vis[now]=0;
}
return dis[ed]!=inf;
}
LL modify(int ed) {
for (int i=ed;pre[i];i=e[pre[i]].x) {
e[pre[i]].w-=1; e[pre[i]^1].w+=1;
}
return (LL)dis[ed]*(dis[ed]>0);
}
LL mcf(int st,int ed) {
LL ret=0;
for (;spfa(st,ed);) ret+=modify(ed);
return ret;
}
LL get(int x,int y) {
if (x*y>n) return 0;
LL ret=(LL)x*y;
for (;ret*(LL)x<=n;) ret=ret*(LL)x;
return ret;
}
int main(void) {
scanf("%d",&n); int sq=sqrt(n);
pre_work(n); LL ans=0;
for (;prime[prime[0]]*2>n;) ans+=prime[prime[0]--];
rep(i,1,prime[0]) {
ans+=get(prime[i],1);
if (prime[i]<=sq) add_edge(0,i,1,0);
else add_edge(i,prime[0]+1,1,0);
}
rep(i,1,prime[0]) {
drp(j,prime[0],1) {
if (prime[i]<=sq&&prime[j]>sq) {
int a=get(prime[i],prime[j]);
int b=get(prime[i],1);
int c=get(prime[j],1);
if (a-b-c>0) {
add_edge(i,j,1,a-b-c);
// printf("%d %d\n", i,j);
}
}
}
}
// printf("%lld\n", ans);
ans+=mcf(0,prime[0]+1);
printf("%lld\n", ans+1);
return 0;
}