[BZOJ3218]A+B problem

5 篇文章 0 订阅
4 篇文章 0 订阅

题目链接BZOJ3218

题目大意
这道题的题面和题目没有任何关系。以及,题目大意略。

分析
1. 显然这道题是一个最大流最小割模型。
2. 设S割的点为黑格,T割的点为白格;对于每个点 i ,由S向i点连边,容量为 bi ;由 i 向T连边,容量为wi;表示使 i 属于S割会损失i作为白格子的优美度;使 i 属于T割会损失i作为黑格子的优美度;
3. 由 i i连边,容量为 pi ;由可以使 i 变得奇♂怪的j i 连边,容量为 INF ;表示若 i 属于S割且j属于T割,那么就会损失 pi 的优美度。
4. 然后最大流最小割跑一遍,用 (bi+wi)maxflow 即为答案;但是边太多 (N2) ,会超时
5. 对于每个点,将它以 ai 为权值,对应到权值线段树上;那么对于 i ,只需要对由 [1..i1] 的点集构成的权值线段树中的区间 [li...ri] 连边就好了,而线段树上的点之间相互连边,叶子节点向其对应的点连边,容量均 INF ,但是这样有 N 棵线段树,边的数量还是N2
6. 这样的话,可以用主席树来减少线段树的点的数量,边的数量就变成 NlogN 了。

上代码

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 5e3 + 10;
const int M = 4e5 + 10;
const int INF = 0x3f3f3f3f;

int n;
inline int read() {
    char ch;
    int ans = 0, neg = 1;
    while (!isdigit(ch = getchar()))
        if (ch == '-') neg = -1;
    while (isdigit(ch))
        ans = ans * 10 + ch - '0', ch = getchar();
    return ans * neg;
}

int SS, TT, cnt;
int head[M], len;
#define id(a) ((a) + 2)
struct Lib {
    int st, to, nxt, flow;
    inline void add(int a, int b, int c) {
        st = a, to = b, flow = c;
        nxt = head[a], head[a] = len++;
    }
} lib[M << 1];
inline void makePath(int a, int b, int c) {
    lib[len].add(a, b, c), lib[len].add(b, a, 0);
}

queue <int> Q;
int dep[M], num[M], pre[M], curHead[M];
void bfs() {
    memset(dep, 0x3f, sizeof(dep));
    dep[TT] = 0, Q.push(TT);
    while (!Q.empty()) {
        int tmp = Q.front(); Q.pop();
        for (int p = head[tmp]; p; p = lib[p].nxt) {
            int now = lib[p].to;
            if (!lib[p ^ 1].flow || dep[now] != INF) continue;
            dep[now] = dep[tmp] + 1, Q.push(now);
        }
    }
}
int augment() {
    int tmp = TT, minn = INF;
    while (tmp != SS) {
        minn = min(minn, lib[pre[tmp]].flow);
        tmp = lib[pre[tmp]].st;
    } tmp = TT;
    while (tmp != SS) {
        lib[pre[tmp]].flow -= minn, lib[pre[tmp] ^ 1].flow += minn;
        tmp = lib[pre[tmp]].st;
    }
    return minn;
}
int ISAP() {
    bfs();
    for (int i = 1; i <= cnt; i++) {
        curHead[i] = head[i];
        if (dep[i] != INF) num[dep[i]]++;
    }
    int ans = 0, tmp = SS;
    while (dep[SS] < INF) {
        if (tmp == TT) ans += augment(), tmp = SS;
        bool flag = false;
        for (int &p = curHead[tmp]; p; p = lib[p].nxt) {
            int now = lib[p].to;
            if (lib[p].flow && dep[now] == dep[tmp] - 1) {
                pre[now] = p, tmp = now, flag = true; break;
            }
        }
        if (!flag) {
            int minn = INF;
            if (!--num[dep[tmp]]) break;
            for (int p = head[tmp]; p; p = lib[p].nxt)
                if (lib[p].flow) minn = min(minn, dep[lib[p].to]);
            if (minn < INF) num[dep[tmp] = minn + 1]++;
            curHead[tmp] = head[tmp];
            if (tmp != SS) tmp = lib[pre[tmp]].st;
        }
    }
    return ans;
}

int T[N], lc[M], rc[M];
inline int modify(int a, int p, int poi) {
    int now = ++cnt, tmp = now;
    int l = 1, r = n, mid;
    while (l < r) {
        mid = (l + r) >> 1;
        if (p <= mid) {
            makePath(now, rc[now] = rc[a], INF);
            makePath(now, lc[now] = ++cnt, INF);
            now = lc[now], a = lc[a], r = mid;
        } else {
            makePath(now, lc[now] = lc[a], INF);
            makePath(now, rc[now] = ++cnt, INF);
            now = rc[now], a = rc[a], l = mid + 1;
        }
    }
    makePath(now, id(poi), INF), makePath(now, a, INF);
    return tmp;
}
void calcPath(int a, int l, int r, int ll, int rr) {
    if (!a || ll > rr) return;
    if (l == ll && r == rr) return makePath(cnt, a, INF);
    int mid = (l + r) >> 1;
    if (rr <= mid) return calcPath(lc[a], l, mid, ll, rr);
    else if (ll > mid) return calcPath(rc[a], mid + 1, r, ll, rr);
    calcPath(lc[a], l, mid, ll, mid), calcPath(rc[a], mid + 1, r, mid + 1, rr);
}

int dsc[N];
struct Blc {
    int a, l, r, b, w, p;
    inline void input() {
        a = read(), b = read(), w = read();
        l = read(), r = read(), p = read();
    }
} blc[N];
void calcDsc() {
    for (int i = 1; i <= n; i++)
        blc[i].input(), dsc[i] = blc[i].a;
    sort(dsc + 1, dsc + n + 1);
    for (int i = 1; i <= n; i++) {
        blc[i].a = lower_bound(dsc + 1, dsc + n + 1, blc[i].a) - dsc;
        blc[i].l = lower_bound(dsc + 1, dsc + n + 1, blc[i].l) - dsc;
        blc[i].r = upper_bound(dsc + 1, dsc + n + 1, blc[i].r) - dsc - 1;
    }
}
void init() {
    n = read(), calcDsc();
    SS = 1, TT = 2, len = 2, cnt = id(n);
    for (int i = 1; i <= n; i++)
        makePath(SS, id(i), blc[i].b), makePath(id(i), TT, blc[i].w);
    for (int i = 1; i <= n; i++) {
        makePath(id(i), ++cnt, blc[i].p);
        calcPath(T[i - 1], 1, n, blc[i].l, blc[i].r);
        T[i] = modify(T[i - 1], blc[i].a, i);
    }
}
int figure() {
    int ans = 0;
    for (int i = 1; i <= n; i++)
        ans += blc[i].b + blc[i].w;
    return ans -= ISAP(), ans;
}
int main() {
    init();
    printf("%d\n", figure());
    return 0;
}

以上

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值