[BZOJ3128]a+b Problem


题解

最小割+主席树优化建图
首先看到每个点只有\(0/1\)两种状态就想到最小割
然后由于有一个限制是点\(i\)是黑点且有符合条件的白点就会减去\(p_i\)
所以我们将\(S\)集合设为黑点集合,\(T\)集合设为白点集合
然后\(S\to i\)流量为\(b_i\) , \(i\to T\)流量为\(w_i\)
然后我们就需要考虑如果一个点选择了黑点那么就要去判断有没有符合条件的白点
所以我们对每个\(i\)新建一个\(i'\)
然后\(i\to i'\)连流量为\(p_i\)的边
\(i'\)向符合条件的\(j\)连流量为\(INF\)的边

由于是求最大值,所以答案就是\(\sum_{b_i+w_i}-\)最小割
这样建图的最小割就可以起到自动分类讨论的作用
例如如果\(i\)为黑点,\(j\)为白点,那么就会产生\(-p_i\)的贡献
这样的话如果我们实际选择了\(i\)为黑点,\(j\)为白点
那么这一路的流量就是\(w_i+b_j+\min(b_i-w_i,p_i,w_j-b_j)\)
这样就自动的起到了分类讨论的作用

然后我们发现一个黑点对于符合条件的白点的要求是一个二维的限制
所以可以考虑主席树优化建图
跟线段树优化建图比较类似
就是在继承上一棵主席树的时候两棵主席树之间要互相连一条边

void insert() {
    t[++tot] = t[now] ; 
    add_edge(tot , now) ; /*注意这里要连边*/
    now = tot ;
}

代码

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define ls (t[now].lsn)
# define rs (t[now].rsn)
const int M = 500005 ;
const int N = 5050 ;
const int INF = 1e9 ;
using namespace std ;

inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

map < int , int > mp ;
int n , tot , S , T , cnt ;
int num = 1 , hea[M] , ans ;
int rt[N] , s[N] , suc[N] , idk[N] , d[M] ;
int a[N] , b[N] , w[N] , lp[N] , rp[N] , p[N] ;

struct E { int nxt , to , dis ; } edge[M * 5] ;
struct Node { int lsn , rsn ; } t[M] ;
inline void Insert_edge(int from , int to , int dis) {
	edge[++num].nxt = hea[from] ; edge[num].to = to ;
	edge[num].dis = dis ; hea[from] = num ;
}
inline void add_edge(int u , int v , int w) {
	Insert_edge(u , v , w) ;
	Insert_edge(v , u , 0) ;
}
inline int Gid(int x) {
	int l = 1 , r = cnt , ret = 0 , mid ;
	while(l <= r) {
		mid = (l + r) >> 1 ;
		if(suc[mid] <= x) l = mid + 1 , ret = mid ;
		else r = mid - 1 ;
	}
	return ret ;
}

void Insert(int id , int x , int l , int r , int &now) {
	t[++tot] = t[now] ; add_edge(tot , now , INF) ; now = tot ;
	if(l == r) { add_edge(now , id , INF) ; return ; }
	int mid = (l + r) >> 1 ;
	if(mid >= x) Insert(id , x , l , mid , ls) ;
	else Insert(id , x , mid + 1 , r , rs) ;
	if(ls) add_edge(now , ls , INF) ; 
	if(rs) add_edge(now , rs , INF) ;
}
void Add(int id , int L , int R , int l , int r , int now) {
	if(now <= n) return ;
	if(l >= L && r <= R) { add_edge(id , now , INF) ; return ; }
	int mid = (l + r) >> 1 ;
	if(mid >= R) Add(id , L , R , l , mid , ls) ;
	else if(mid < L) Add(id , L , R , mid + 1 , r , rs) ;
	else Add(id , L , mid , l , mid , ls) , Add(id , mid + 1 , R , mid + 1 , r , rs) ;
}
inline bool bfs() {
	queue < int > q ; q.push(S) ;
	memset(d , 0 , sizeof(d)) ; d[S] = 1 ;
	while(!q.empty()) {
		int u = q.front() ; q.pop() ;
		for(int i = hea[u] ; i ; i = edge[i].nxt) {
			int v = edge[i].to ;
			if(!d[v] && edge[i].dis > 0) {
				d[v] = d[u] + 1 ;
				q.push(v) ; if(v == T) return true ;
			}
		}
	}
	return d[T] ;
}
int dfs(int u , int dis) {
	if(u == T || !dis) return dis ; int sum = 0 ;
	for(int i = hea[u] ; i ; i = edge[i].nxt) {
		int v = edge[i].to ;
		if(d[v] == d[u] + 1 && edge[i].dis > 0) {
			int diss = dfs(v , min(dis , edge[i].dis)) ;
			if(diss > 0) {
				edge[i].dis -= diss ; edge[i ^ 1].dis += diss ;
				dis -= diss ; sum += diss ; if(!dis) break ;
			}
		}
	}
	if(!sum) d[u] = -1 ; return sum ;
}
inline int dinic() {
	int tmp = 0 ;
	while(bfs())
		tmp += dfs(S , INF) ;
	return tmp ;
}
int main() {
	n = read() ;
	for(int i = 1 ; i <= n ; i ++) {
		a[i] = read() ; b[i] = read() ; w[i] = read() ; 
		lp[i] = read() ; rp[i] = read() ; p[i] = read() ;
		add_edge(S , ++tot , b[i]) ;
		s[i] = a[i] ; ans += b[i] + w[i] ;
	}
	rt[0] = tot ;
	sort(s + 1 , s + n + 1) ;
	for(int i = 1 ; i <= n ; i ++) {
		if(i == 1 || s[i] != s[i - 1]) ++ cnt ;
		mp[s[i]] = cnt ; suc[cnt] = s[i] ;
	}
	for(int i = 1 ; i <= n ; i ++) 
		a[i] = mp[a[i]] ;
	for(int i = 1 ; i <= n ; i ++) {
		rt[i] = rt[i - 1] ;
		Insert(i , a[i] , 0 , cnt , rt[i]) ;
	}
	for(int i = 1 ; i <= n ; i ++) {
		idk[i] = ++ tot ;
		add_edge(i , idk[i] , p[i]) ;
	}
	T = tot + 1 ;
	for(int i = 1 ; i <= n ; i ++) 
		add_edge(i , T , w[i]) ;
	suc[0] = -1 ;
	for(int i = 1 , l , r ; i <= n ; i ++) {
		l = Gid(lp[i]) , r = Gid(rp[i]) ;
		if(lp[i] != suc[l]) ++ l ;
		if(l > r) continue ;
		Add(idk[i] , l , r , 0 , cnt , rt[i - 1]) ;
	}
	printf("%d\n",ans - dinic()) ;
	return 0 ;
}
posted @ 2019-04-26 21:46  beretty  阅读(248)  评论(0编辑  收藏  举报