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

75 篇文章 1 订阅
39 篇文章 0 订阅

任意两个符合条件的数字,指数之和只差1,因此形成了二分图,每个点的可选择次数为 bi ,因此考虑费用流,
<si,cap:bi,cost:0>(ai <script type="math/tex" id="MathJax-Element-284"> (a_i的指数之和为奇数</script>
<it,cap:bi,cost:0>(ai) <script type="math/tex" id="MathJax-Element-285"> (a_i的指数之和为偶数)</script>
<ij,cap:,cost:cicj>(aiaj) <script type="math/tex" id="MathJax-Element-286"> (a_i和a_j可匹配)</script>

由于要求总价值和不得小于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

3
2 4 8
2 200 7
-1 -2 1

Sample Output

4

HINT

n200ai109bi105ci105

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值