首页 > 代码库 > bzoj3218 a + b Problem

bzoj3218 a + b Problem

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3218

【题解】

按照最小割建模,S->x连流量为white的边,x->T连流量为black的边,割掉S->x表示取黑色,割掉x->T表示取白色,一开始加上所有贡献。

考虑奇怪的格子,一定是对于点对$(x,y)$,$x$选了黑,$y$选了白,且$y$满足条件,才会扣除$p_i$的贡献。

暴力的想法,枚举每一对$(x,y)$关系,连边y->x,流量$p_i$。

我们发现上面的做法有些问题,就是如果$x$是奇怪的点,且和很多白点有关系,其实会被算了很多次。

这个问题好办,我们把每个点$x$多建一个$x‘$,从$i‘$向$x$连边,流量为$p_i$。

从$j$向$i‘$连边,流量为$+\infty$,表示这条边不能被割。

我们建完图,发现,卡空间。。。

那怎么办啊?

发现奇怪的点的关系其实是一个二维限制,可以用主席树来求出,可是求出没有什么用,边还是很多,考虑把这棵主席树全部放到建的网络流图里。

主席树的$root_i$的区间$[l,r]$表示$a_j$在$[l,r]$内,且$j < i$的点。

那么每次我们主席树只会新加一条链,我们把这条链自下而上连边,主席树上的边流量均为$+\infty$,表示不能割掉

那么对于每个底层区间$[p, p]$,如果有$a_j$在$p$中,连边j->这个区间的id。

当然这样可能每个底层区间有一坨,那么就从主席树的$rt_{i-1}$的这个地方向$rt_i$的这个地方连边即可。

每次加入点前,先查询区间$[l_i,r_i]$,把区间$[l_i,r_i]$的$O(logn)$个代表节点拉出来,跟$i‘$连边。

这样边数就在n*log级别,可以承受。

空间开的时候稍微注意点即可。

技术分享
# include <queue>
# include <vector>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N = 5e3 + 10, M = 1.5e6 + 10;
const int mod = 1e9+7, inf = 1e9;
const int Ms = 3e6 + 5;

int n;
vector<int> ps;
struct pa {
    int a, b, w, l, r, p;
    pa() {}
    pa(int a, int b, int w, int l, int r, int p) : a(a), b(b), w(w), l(l), r(r), p(p) {}
}p[N];

int idx = 0, S, T;
int head[M], nxt[M], to[M], tot = 1, flow[M];
inline void add(int u, int v, int fl) {
    ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; flow[tot] = fl;
}
inline void adde(int u, int v, int fl) {
//    printf("%d ---> %d, flow = %d\n\n", u, v, fl); 
    add(u, v, fl); add(v, u, 0);
}

int rt[N], id[N];
struct CMT {
    int ch[Ms][2], id[Ms], siz;
    inline void set() {
        siz = 0;
        memset(ch, 0, sizeof ch);
        memset(id, 0, sizeof id);
    }
    inline void edt(int &x, int y, int l, int r, int d, int lk) {
        x = ++siz, id[x] = ++idx;
        ch[x][0] = ch[y][0], ch[x][1] = ch[y][1];
        if(l == r) {
            adde(lk, id[x], inf);
            if(y) adde(id[y], id[x], inf);
            return ;
        }
        int mid = l+r>>1;
        if(d <= mid) edt(ch[x][0], ch[y][0], l, mid, d, lk);
        else edt(ch[x][1], ch[y][1], mid+1, r, d, lk);
        if(ch[x][0]) adde(id[ch[x][0]], id[x], inf);
        if(ch[x][1]) adde(id[ch[x][1]], id[x], inf);
    }
    inline void link(int x, int l, int r, int L, int R, int lk) {
        if(!x) return ;
        if(L <= l && r <= R) {
            adde(id[x], lk, inf);
            return ;
        }
        int mid = l+r>>1;
        if(L <= mid) link(ch[x][0], l, mid, L, R, lk);
        if(R > mid) link(ch[x][1], mid+1, r, L, R, lk);
    }
}t;


namespace MF {
    queue<int> q;
    int c[M], cur[M];
    inline bool bfs() {
        while(!q.empty()) q.pop(); 
        for (int i=1; i<=idx; ++i) c[i] = -1;
        q.push(S); c[S] = 1;
        while(!q.empty()) {
            int top = q.front(); q.pop();
            for (int i=head[top]; i; i=nxt[i]) {
                if(c[to[i]] != -1 || !flow[i]) continue;
                c[to[i]] = c[top] + 1;
                q.push(to[i]);
                if(to[i] == T) return 1;    
            }
        }
        return 0;
    }
    inline int dfs(int x, int low) {
        if(x == T) return low; 
        int r = low, fl;
        for (int i=cur[x]; i; i=nxt[i]) {
            if(c[to[i]] != c[x] + 1 || !flow[i]) continue; 
            fl = dfs(to[i], min(r, flow[i]));
            flow[i] -= fl; flow[i^1] += fl; r -= fl;
            if(flow[i] > 0) cur[x] = i; 
            if(!r) return low;
        }
        if(low == r) c[x] = -1;
        return low-r;
    }
    inline int main() {
        int ret = 0;
        while(bfs()) {
            for (int i=1; i<=idx; ++i) cur[i] = head[i];
            ret += dfs(S, inf);
        }
        return ret;
    }
}

int main() {
    int sum = 0;
    scanf("%d", &n);
    S = n+n+1, T = n+n+2;
    for (int i=1; i<=n; ++i) {
        scanf("%d%d%d%d%d%d", &p[i].a, &p[i].b, &p[i].w, &p[i].l, &p[i].r, &p[i].p);
        ps.push_back(p[i].a), ps.push_back(p[i].l), ps.push_back(p[i].r);
        adde(S, i, p[i].w); adde(i, T, p[i].b);    
        adde(i+n, i, p[i].p);
        sum = sum + p[i].w + p[i].b;
    }
    sort(ps.begin(), ps.end()); ps.erase(unique(ps.begin(), ps.end()), ps.end());
    for (int i=1; i<=n; ++i) { 
        p[i].a = lower_bound(ps.begin(), ps.end(), p[i].a) - ps.begin() + 1;
        p[i].l = lower_bound(ps.begin(), ps.end(), p[i].l) - ps.begin() + 1;
        p[i].r = lower_bound(ps.begin(), ps.end(), p[i].r) - ps.begin() + 1; 
    }
    idx = n+n+2; t.set();
    for (int i=1; i<=n; ++i) {
        t.link(rt[i-1], 1, ps.size(), p[i].l, p[i].r, i+n);
        t.edt(rt[i], rt[i-1], 1, ps.size(), p[i].a, i);
    }
    cout << sum - MF::main();
    return 0;
}
View Code

 

bzoj3218 a + b Problem