BZOJ 3218|UOJ 77|A + B Problem|最大流|可持久化线段树

39 篇文章 0 订阅
27 篇文章 0 订阅

吐槽

这题目真的很吸引人
样例不能复制真是可惜
题目的方格染色以及奇怪的节点让我想起了以前看过的一篇博文,讲最小割的。。
于是这道题就最小割。。。

题解

好吧假设我们不知道这道题要最小割
看着题目给的式子我们需要对它做♂点事情。

maxibi+ibiipi

我们令 xi=1 表示点i为黑, xi=0 表示点i为白。那么上面的式子可以写成:
max{max{xi,0}bi+max{1xi,0}wimax{xixj,0}pi}

先在这个式子就与线性规划转化为最小割的式子长的比较像了,但是转化的时候要求最小化且函数内部不能有减号,所以我们还要修改这个式子。
wi+bimin{max{1xi,0}bi+max{xi,0}wi+max{xixj,0}pi}

这个式子符合我们的要求,按照这个式子构图就好了。
需要注意的是这个式子有约束条件,发现对于每个 xi max{xixj,0} 的值不能超过1,因为如果i奇怪 pi 的值只用计一次。
因此我们这么构图。
源点连向 i ,边权为wi i 连向汇点,边权为bi。对于奇怪的方格,如果不限制 (i,j) 的次数,那么我们直接将 j 指向 i,边权为 pi ,但是不行,所以建立一个节点 i ,连向 i ,边权为pi,将满足条件的 j 指向 i,边权无限大。

图构完了,但是满足条件的 (i,j) 可能是 O(n2) 的,所以我们考虑请出权值线段树。
如果 j i 的区间里,那么考虑 j 指向线段树中对应的节点,线段树中表示的 i 的区间的点指向 i
但是没有考虑 j<i ,所以我们需要可持久化线段树限制区间。
没有图,可以看PoPoQQQ神的文。
那么构建可持久化线段树时,我们要插入 ai ,所以在插入过程中改图,每新建一条树边自然要在图中对应加边,还有 i 也要指向包含 ai 的节点。
然后查询可持久化线段树的 [li,ri] 区间,并将正好整个在这个区间内的点全部连向 i
于是问题解决了。

Coding Summary

为什么别人的程序都这么快。。
难道姿势不一样QAQ

真的是痛苦。。
各种脑抽写错,程序重写了2遍。。
原来SegTree的缺省构造函数里面包括了自增id,然而operator new里面定义的大数组也会自增id。
然后不说占用了大量无用id,各种爆数组。。之前还奇怪为什么会RE。。
有时候不能手贱,缺省构造函数还是要谨慎一些。

样例文本:

10
0 1 7 3 9 2
7 4 0 9 10 5
1 0 4 2 10 2
7 9 1 5 7 2
6 3 5 3 6 2
6 6 4 1 8 1
6 1 6 0 6 5
2 2 5 0 9 3
5 1 3 0 2 5
5 6 7 1 1 2

Summary

  1. 网络中若存在大量的无限容量的边,可以调整这些边使边数更少。
  2. 由题意得到的数学表达式若存在2种对立状态和其他与这2种状态有关的状态,可以采用最小割,将数学表达式改写为最小割线性规划表达形式。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 8192, M = 262144, inf = 1000000000, EDGE = 1048576;
struct SegTree {
    static int timer;
    static void put_callback(SegTree *node, SegTree *new_node, int f);
    static void get_callback(SegTree *node, int x);
    SegTree *lc, *rc;
    int v, s;
    SegTree() { lc = rc = NULL; v = s = 0; }
    SegTree(SegTree *a, SegTree *b, int c) : lc(a), rc(b), v(c), s(++timer) {}
    void *operator new(size_t) {
        static SegTree pool[M], *C = pool;
        return C++;
    }
    SegTree *put(int l, int r, int t, int f) {
        int mid = l + r >> 1;
        SegTree *rt;
        if (l == r) rt = new SegTree(NULL, NULL, v + 1);
        else if (t <= mid) rt = new SegTree(lc->put(l, mid, t, f), rc, v + 1);
        else rt = new SegTree(lc, rc->put(mid + 1, r, t, f), v + 1);
        put_callback(this, rt, f);
        return rt;
    }
    void get(int l, int r, int ql, int qr, int x) {
        int mid = l + r >> 1;
        if (!v) return;
        if (ql == l && r == qr) {
            get_callback(this, x);
            return;
        }
        if (ql > mid) rc->get(mid + 1, r, ql, qr, x);
        else if (qr <= mid) lc->get(l, mid, ql, qr, x);
        else lc->get(l, mid, ql, mid, x), rc->get(mid + 1, r, mid + 1, qr, x);
    }
} *tree[N];
int SegTree::timer = 0;
struct MaxFlow {
    int s, t, cnt;
    int head[M], next[EDGE], w[EDGE], to[EDGE], level[M];
    MaxFlow(int _s, int _t) : cnt(0), s(_s), t(_t) {
        memset(head, -1, sizeof head);
    }
    void add(int u, int v, int c) {
        next[cnt] = head[u]; to[cnt] = v; w[cnt] = c; head[u] = cnt++;
        next[cnt] = head[v]; to[cnt] = u; w[cnt] = 0; head[v] = cnt++;
    }
    bool bfs() {
        static int q[M];
        memset(level, -1, sizeof level);
        int f = 0, r = 0;
        q[r++] = s; level[s] = 0;
        while (f < r) {
            int u = q[f++];
            for (int i = head[u]; i != -1; i = next[i])
                if (w[i] > 0 && level[to[i]] == -1) {
                    level[to[i]] = level[u] + 1;
                    q[r++] = to[i];
                }
        }
        return level[t] != -1;
    }
    int dfs(int k, int low) {
        if (k == t) return low;
        int res = 0, tmp, i;
        for (i = head[k]; i != -1 && res < low; i = next[i])
            if (level[to[i]] == level[k] + 1 && w[i] > 0) {
                tmp = dfs(to[i], min(low - res, w[i]));
                w[i] -= tmp; w[i^1] += tmp; res += tmp;
            }
        if (!res) level[k] = -1;
        return res;
    }
    ll solve() {
        ll ans = 0;
        while (bfs()) ans += dfs(s, inf);
        return ans;
    }
} *maxflow;

void SegTree::put_callback(SegTree *t, SegTree *nt, int f) {
    maxflow->add(f, nt->s, inf);
    maxflow->add(t->s, nt->s, inf);
}
void SegTree::get_callback(SegTree *t, int x) {
    maxflow->add(t->s, x, inf);
}

int main() {
    #define p2(i) ((i)*2)
    #define p1(i) ((i)*2-1)
    int i, a, b, w, l, r, p, n;
    ll ans = 0;
    scanf("%d", &n);
    maxflow = new MaxFlow(0, M - 1);
    SegTree::timer = p2(n);
    tree[0] = new SegTree(NULL, NULL, 0);
    tree[0]->lc = tree[0]->rc = tree[0];
    for (i = 1; i <= n; i++) {
        scanf("%d%d%d%d%d%d", &a, &b, &w, &l, &r, &p);
        maxflow->add(maxflow->s, p1(i), w);
        maxflow->add(p1(i), maxflow->t, b);
        tree[i] = tree[i - 1]->put(0, inf, a, p1(i));
        tree[i - 1]->get(0, inf, l, r, p2(i));
        maxflow->add(p2(i), p1(i), p);
        ans += w + b;
    }
    printf("%lld", ans - maxflow->solve());

    return 0;
}

数据生成器

写了个最低难度的数据生成器。。真是不写没法调试。。

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;

int random(int sz) {
    return ((double)rand())/RAND_MAX*sz+1;
}

int main() {
    freopen("input.in", "w", stdout);
    int n = 5000, maxA = 1e9, maxV = 2e5, maxP = 3e5;
    srand(time(NULL));
    printf("%d\n", n);
    for (int i = 1; i <= n; i++) {
        int c = random(maxA), x = random(min(c, maxA - c));
        printf("%d %d %d %d %d %d\n", random(maxA), random(maxV), random(maxV), c - x, c + x, random(maxP));
    }

    return 0;
}

题目

http://uoj.ac/problem/77
a + b Problem Page 1

a + b Problem Page 2
相关博客:http://blog.csdn.net/regina8023/article/details/42873403
http://www.cnblogs.com/snake-hand/p/3144807.html
http://blog.csdn.net/PoPoQQQ/article/details/42557217

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值