BZOJ 3308: 九月的咖啡店

Description

深绘里在九份开了一家咖啡让,如何调配咖啡民了她每天的头等大事。

我们假设她有N种原料,第i种原料编号为i,调配一杯咖啡则需要在这里若干种兑在一起。

不过有些原料不能同时在一杯中,如果两个编号为i,j的原料,当且仅当i与j互质时,才能兑在同一杯中。

现在想知道,如果用这N种原料来调同一杯咖啡,使用的原料编号之和最大可为多少。

Input

一个数字N

Output

如题

Sample Input

10

Sample Output

30

HINT

1<=N<=200000

Solution

搬运一下PoPoQQQ大神的题解:

题目大意:在[1,n]区间内选择一些数,使得这些数两两互质,求这些数的和的最大值

容易发现对于一个最优解,每个质数存在且仅存在于一个数中。(废话。

但是有可能一个数中存在多个质数

下面是两个结论:

1.一个数中最多存在两个不同的质数

2.这两个质数一个<√n,一个>√n

我完全不会证明这两个结论,这两个结论都是官方题解里的

然后就好办了,我们对于<√n的质数和>√n的质数建立二分图,求最大费用最大流即可

但是这样会T掉,因为图太大了

因此我们有两个剪枝:

1.对于>2n的质数,一定单独存在于解集中,不用扔进二分图跑了

2.如果某两个质数组合起来不如分别取最大后加起来,就不加这条边

加了之后基本就能过了……20W的点跑了9s+

这个题是PE的355 听说PE的那群人跑的都是模拟退火?

据说巨快……

关于二分图建法,搬运一下Oxer爷的题解:

源点S向小于根n的质数连一条容量为1费用为0的边

大于根n的质数向汇点T连一条容量为1费用为0的边

小于根n的质数a向大于根n的质数b连一条容量为1费用为Vab-Va-Vb的边

Va表示单独选a的最大收益=a^(lgn/lga)

Vb表示单独选b的最大收益=b

Vab表示同时选a和b的最大收益=a^(lg(n/b)/lga)*b

Orz。。。调了半天发现跑费用流的时候不一定需要最大流。最大费用就可以了。(WTF。。。以前把费用流和最小费用最大流混了。。。)

Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long LL;
const LL INF = ~0uLL>>3;
queue<int> Q;
namespace Flow {
    struct edge_type {
        int to, next;
        LL r, c;
    } edge[2000050];
    int cnte = 1, h[200005];
    void ins(int x, int y, LL r, LL c) {
        edge[++cnte].to = y; edge[cnte].next = h[x]; edge[cnte].r = r; edge[cnte].c = c; h[x] = cnte;
        edge[++cnte].to = x; edge[cnte].next = h[y]; edge[cnte].r = 0; edge[cnte].c = -c; h[y] = cnte;
    }
    int n, m, S, T;
    int pre[200005];
    LL dis[200005], a[200005];
    bool vis[200005];
    bool SPFA(LL &flow, LL &cost) {
        for (int i = S; i <= T; ++i) {
            dis[i] = INF;
            vis[i] = false;
        }
        a[S] = INF; dis[S] = 0; Q.push(S); vis[S] = true;
        int now;
        while (!Q.empty()) {
            now = Q.front(); vis[now] = false; Q.pop();
            for (int i = h[now]; i; i = edge[i].next) {
                if (!edge[i].r) continue;
                if (dis[edge[i].to] > dis[now] + edge[i].c) {
                    pre[edge[i].to] = i;
                    dis[edge[i].to] = dis[now] + edge[i].c;
                    a[edge[i].to] = min(a[now], edge[i].r);
                    if (!vis[edge[i].to]) {
                        vis[edge[i].to] = true;
                        Q.push(edge[i].to);
                    }
                }
            }
        }
        if (dis[T] >= 0)
            return false;
        flow += a[T];
        cost += a[T] * dis[T];
        for (now = T; now != S; now = edge[pre[now]^1].to) {
            edge[pre[now]].r -= a[T];
            edge[pre[now]^1].r += a[T];
        }
        return true;
    }
    LL MCMF() {
        LL flow = 0, cost = 0;
        while (SPFA(flow, cost));
        return cost;
    }
}
using Flow::ins;
using Flow::MCMF;
using Flow::S;
using Flow::T;
int N;
int P[200005], vis[200005], tot = 0, P2, P3;
void GetPrime() {
    for (int i = 2; i <= N; ++i) {
        if (!vis[i]) {
            P[++tot] = i;
        }
        for (int j = 1; j <= tot && (LL)P[j] * i <= N; ++j) {
            vis[P[j] * i] = true;
            if (i % P[j] == 0) break;
        }
    }
}
LL Calc(int n, int x) {
    LL tmp = 1;
    while (tmp * x <= n) tmp *= x;
    return tmp;
}
LL ans = 0;
int main() {
    int i, j;
    scanf("%d", &N);
    GetPrime();
    S = 0; T = tot + 1;
    for (i = 1; i <= tot && P[i] * 2 <= N; ++i) {
        if ((LL)P[i] * P[i] <= N) {
            ins(S, i, 1, 0);
            ans += Calc(N, P[i]);
        }
        else {
            ins(i, T, 1, 0);
            ans += P[i];
        }
    }
    for (P3 = i; i <= tot; ++i) ans += P[i];
    for (P2 = 1; P2 <= tot && (LL)P[P2] * P[P2] <= N; ++P2);
    for (i = 1; i < P2; ++i)
        for (j = P2; j < P3; ++j) {
            if ((LL)P[i] * P[j] > N) break;
            LL tmp = Calc(N / P[j], P[i]) * P[j] - (Calc(N, P[i]) + P[j]);
            if (tmp > 0)
                ins(i, j, 1, -tmp);
        }
    ans -= MCMF(); ++ans;
    printf("%lld", ans);
    return 0;
}