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; }