BZOJ 4514|SDOI 2016|数字配对|筛法|费用流

来源:互联网 发布:如何找回淘宝聊天记录 编辑:程序博客网 时间:2024/06/07 02:11

任意两个符合条件的数字,指数之和只差1,因此形成了二分图,每个点的可选择次数为bi,因此考虑费用流,
<si,cap:bi,cost:0>(ai
<it,cap:bi,cost:0>(ai)
<ij,cap:,cost:cicj>(aiaj)

由于要求总价值和不得小于0,因此在最大费用最大流时,如果某一次增广会使得总价值和小于0,那么就只增广到刚好不小于0的时候就可以结束了。

#include <cstdio>#include <cstring>#include <queue>#include <algorithm>using namespace std;typedef long long ll;#define FOR(i,j,k) for(i=j;i<=k;++i)const int inf = 0x7f7f7f7f, N = 1000, M = 80005;namespace CostFlow {    int q[M * 16], h[N], p[M], v[M], w[M], vis[N], pre[N];    int cnt = 1, tot, s, t;    ll ans, dis[N], c[M];    void add(int x, int y, int z, ll d) {        p[++cnt] = h[x]; v[cnt] = y; w[cnt] = z; c[cnt] = d; h[x] = cnt;        p[++cnt] = h[y]; v[cnt] = x; w[cnt] = 0; c[cnt] = -d; h[y] = cnt;    }    bool spfa() {        int i, u, f = 0, r = 0;        memset(dis, 0x7f, sizeof dis);        dis[s] = 0; q[r++] = s; pre[s] = -1;        while (f < r) {            u = q[f++]; vis[u] = 0;            for (i = h[u]; i; i = p[i])                if (w[i] && dis[v[i]] > dis[u] + c[i]) {                    dis[v[i]] = dis[u] + c[i];                    pre[v[i]] = i ^ 1;                    if (!vis[v[i]]) {                        vis[v[i]] = 1;                        q[r++] = v[i];                    }                }        }        return dis[t] != 0x7F7F7F7F7F7F7F7Fll;    }    bool end() {        int u, sum = inf;        for (u = pre[t]; u != -1; u = pre[v[u]])            sum = min(sum, w[u ^ 1]);        if (ans + dis[t] * sum <= 0) {            for (u = pre[t]; u != -1; u = pre[v[u]])                w[u] += sum, w[u ^ 1] -= sum;            ans += dis[t] * sum;            tot += sum;            return 1;        } else {            tot -= ans / dis[t];            return 0;        }    }    void solve() {        ans = tot = 0;        while (spfa() && end());    }}namespace Sleve {    int vis[32001], p[32001], isprime[32001], cnt = 0;    bool ok(int i, int j) {        if (i < j) swap(i, j);        if (!j || i % j) return 0;        else for (int k = 0; k < cnt && i / j > p[k]; ++k)            if (i / j % p[k] == 0) return 0;        return 1;    }    void get_primes(int n) {        int i, j;        FOR(i,2,n) {            if (!vis[i]) p[cnt++] = i, isprime[i] = 1;            for (j = 0; j < cnt && i * p[j] <= n; ++j) {                vis[i * p[j]] = 1;                if (i % p[j] == 0) break;            }        }    }}int a[N], b[N], odd[N], even[N];ll c[N];int main() {    int i, j, k, sum, o = 0, e = 0, n;    scanf("%d", &n);    FOR(i,1,n) scanf("%d", a + i);    FOR(i,1,n) scanf("%d", b + i);    FOR(i,1,n) scanf("%lld", c + i);    Sleve::get_primes(32000);    FOR(i,1,n) {        for (sum = j = 0; j < Sleve::cnt; ++j)            for (k = a[i]; k % Sleve::p[j] == 0; k /= Sleve::p[j])                sum ^= 1;        if (sum) odd[++o] = i; else even[++e] = i;    }    FOR(i,1,o) FOR(j,1,e)        if (Sleve::ok(a[odd[i]], a[even[j]]))            CostFlow::add(odd[i], even[j], inf, -c[odd[i]] * c[even[j]]);    CostFlow::s = n + 1; CostFlow::t = n + 2;    FOR(i,1,o) CostFlow::add(CostFlow::s, odd[i], b[odd[i]], 0);    FOR(i,1,e) CostFlow::add(even[i], CostFlow::t, b[even[i]], 0);    CostFlow::solve();    printf("%d", CostFlow::tot);    return 0;}

4514: [Sdoi2016]数字配对

Description

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

Input

第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。

Output

一行一个数,最多进行多少次配对

Sample Input

32 4 82 200 7-1 -2 1

Sample Output

4

HINT

n200ai109bi105ci105

0 0
原创粉丝点击