[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 ;
}
[BZOJ3128]a+b Problem的更多相关文章
- 1199 Problem B: 大小关系
求有限集传递闭包的 Floyd Warshall 算法(矩阵实现) 其实就三重循环.zzuoj 1199 题 链接 http://acm.zzu.edu.cn:8000/problem.php?id= ...
- No-args constructor for class X does not exist. Register an InstanceCreator with Gson for this type to fix this problem.
Gson解析JSON字符串时出现了下面的错误: No-args constructor for class X does not exist. Register an InstanceCreator ...
- C - NP-Hard Problem(二分图判定-染色法)
C - NP-Hard Problem Crawling in process... Crawling failed Time Limit:2000MS Memory Limit:262144 ...
- Time Consume Problem
I joined the NodeJS online Course three weeks ago, but now I'm late about 2 weeks. I pay the codesch ...
- Programming Contest Problem Types
Programming Contest Problem Types Hal Burch conducted an analysis over spring break of 1999 and ...
- hdu1032 Train Problem II (卡特兰数)
题意: 给你一个数n,表示有n辆火车,编号从1到n,入站,问你有多少种出站的可能. (题于文末) 知识点: ps:百度百科的卡特兰数讲的不错,注意看其参考的博客. 卡特兰数(Catalan):前 ...
- BZOJ2301: [HAOI2011]Problem b[莫比乌斯反演 容斥原理]【学习笔记】
2301: [HAOI2011]Problem b Time Limit: 50 Sec Memory Limit: 256 MBSubmit: 4032 Solved: 1817[Submit] ...
- [LeetCode] Water and Jug Problem 水罐问题
You are given two jugs with capacities x and y litres. There is an infinite amount of water supply a ...
- [LeetCode] The Skyline Problem 天际线问题
A city's skyline is the outer contour of the silhouette formed by all the buildings in that city whe ...
随机推荐
- java多线程-多线程常识
线程和进程的区别是什么?进程是一个正在运行的软件程序,打开资源管理器可以看到好多正在运行的进程,而线程则是程序中的顺序控制流,只能使用分配给程序的资源和环境.一个进程至少存在一个线程(主线程). 在j ...
- MYSQL进阶学习笔记十三:MySQL 内存优化!(视频序号:进阶_31)
知识点十四:MySQL 内存的优化(31) 一.优化MySQL SERVER 7组后台进程: masterthread:主要负责将脏缓存页刷新到数据文件,执行purge操作,触发检查点,合并插入缓冲区 ...
- ubuntu的ufw如何开放特定端口?
ubuntu的ufw如何开放特定端口? 1.安装 sudo apt-get install ufw 2.开启 sudo ufw enable 默认关闭外部访问 sudo ufw default den ...
- UVA-11892(组合游戏)
题意: 给n堆石子,每堆有ai个,每次可以取每堆中任意数目的石子;但是上一次操作的人没有将一堆全部取走,那么下一个人还要在那一堆取; 思路: 每次取到这堆就剩一个的策略; AC代码: #include ...
- adb pull / push
刚才搞了半天想pull,就是pull不成,如图: 看出哪里有问题了吗? 问题就是我不该在shell里面运行adb pull! 正确的做法: 在任意一处打开命令行比如图中的桌面, adb pull /s ...
- c++11 右值引用和移动语义
什么是左值.右值 最常见的误解: 等号左边的就是左值,等号右边的就是右值 左值和右值都是针对表达式而言的, 左值是指表达式结束后依然存在的持久对象 右值是指表达式结束时就不再存在的临时对象区分: 能对 ...
- 从MySQL获取数据
安装 PM> install-package newtonsoft.json PM> install-package mysql.data string connectionString ...
- VMware桥接模式选择宿主机物理网卡
当宿主机有多块物理网卡时,VMware桥接模式也要根据情况选择使用的物理网卡. 比如宿主机有两块物理网卡,一个连外网,一个连内网,如果想与内网组成局域网就需要选择宿主机的内网网卡,反之选择外网网卡,当 ...
- 一、MyBatis简介
1.发展历史 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBa ...
- Ubuntu 16.04使用chrome闪屏
使用Chrome的时候上端经常出现闪动的情况, 但是速度特别快, 根本无法截图, 感觉特别扎心, 以为自己的电脑出现问题了或者显卡驱动出现问题了, 后来才发现问题, 只需要关闭Chrome的硬件加速就 ...