bzoj3308 九月的咖啡店 费用流

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;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/80438333
今日推荐