题意
深绘里在九份开了一家咖啡让,如何调配咖啡民了她每天的头等大事我们假设她有N种原料,第i种原料编号为i,调配一杯咖啡则需要在这里若干种兑在一起。不过有些原料不能同时在一杯中,如果两个编号为i,j的原料,当且仅当i与j互质时,才能兑在同一杯中。现在想知道,如果用这N种原料来调同一杯咖啡,使用的原料编号之和最大可为多少。
n<=200000
分析
有个很不那么显然的结论就是,如果一个数出现了,那么其最多只包含两个素因数,且这两个素因数一个
<n√
<script type="math/tex" id="MathJax-Element-223"><\sqrt n</script>,一个
>n√
这个就真的证不出来的说。
那么直接把素数分成二分图直接费用流就好了。
这里有几个剪枝:
如果一个素数大于n/2则直接算入答案。
先把每个素数的结果算进答案,如果
Vab>Va+Vb
的话再在a和b之间连
Vab−Va−Vb
的边。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=200005;
const int inf=0x7fffffff;
int n,val[N],cnt,prime[N],tot,last[N],s,t,dis[N],pre[N];
struct edge{int to,from,c,w,next;}e[N*5];
queue <int> q;
bool not_prime[N],vis[N];
LL ans;
void get_prime(int n)
{
for (int i=2;i<=n;i++)
{
if (!not_prime[i]) prime[++tot]=i;
for (int j=1;j<=tot&&i*prime[j]<=n;j++)
{
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
int get_val(int x,int y)
{
if (x*y>n) return 0;
int w=x;
while ((LL)w*x*y<=(LL)n) w*=x;
return w*y;
}
void addedge(int u,int v,int c,int w)
{
e[++cnt].from=u;e[cnt].to=v;e[cnt].c=c;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].from=v;e[cnt].to=u;e[cnt].c=0;e[cnt].w=-w;e[cnt].next=last[v];last[v]=cnt;
}
bool spfa()
{
for (int i=s;i<=t;i++) dis[i]=-inf;
dis[s]=0;vis[s]=1;q.push(s);
while (!q.empty())
{
int u=q.front();q.pop();
for (int i=last[u];i;i=e[i].next)
if (e[i].c&&dis[u]+e[i].w>dis[e[i].to])
{
dis[e[i].to]=dis[u]+e[i].w;
pre[e[i].to]=i;
if (!vis[e[i].to]) q.push(e[i].to),vis[e[i].to]=1;
}
vis[u]=0;
}
if (dis[t]==-inf) return 0;
else return 1;
}
void mcf()
{
if (dis[t]>0) ans+=dis[t];
int x=t;
while (x!=s)
{
e[pre[x]].c--;
e[pre[x]^1].c++;
x=e[pre[x]].from;
}
}
int main()
{
scanf("%d",&n);
get_prime(n);
int w=sqrt(n);
while (prime[tot]>n/2) ans+=prime[tot],tot--;
s=0;t=tot+1;cnt=1;
for (int i=1;i<=tot;i++)
{
ans+=val[i]=get_val(prime[i],1);
if (prime[i]<=w) addedge(s,i,1,0);
else addedge(i,t,1,0);
}
for (int i=1;i<=tot&&prime[i]<=w;i++)
for (int j=tot;j&&prime[j]>w;j--)
{
int v=get_val(prime[i],prime[j]);
if (v>val[i]+val[j]) addedge(i,j,1,v-val[i]-val[j]);
}
while (spfa()) mcf();
printf("%lld",ans+1);
return 0;
}