LOJ 6032 「雅礼集训 2017 Day2」水箱
来源:互联网 发布:ipad的蜂窝移动数据 编辑:程序博客网 时间:2024/09/21 08:52
线段树合并+树状数组
把0设成-1,问题就变成求最大前缀和。
考虑一个DP,记f[i]表示i隔板隔住了水,i之前最多满足多少条件。转移的时候枚举j表示[j,i]能是一个以j,i为左右端点的装水区间。
这样的问题是每次从新的i扫到一个j都要合并一遍区间里的所有标记,也就是一个区间会被合并多次。然而能够证明,不同的装水区间不超过O(n)个,且它们之间不会有交(端点可能相同)。
因此先找出这些区间,线段树维护一个区间的所有条件,求最大前缀和,合并就是直接线段树合并,这样总O(nlogn)。
#include<cstdio>#include<vector>#include<cstring>#include<algorithm>#define N 200005#define lowbit(_i) (_i&-_i)#define mkp(_i,_j) make_pair(_i,_j)using namespace std;namespace runzhe2000{ const int INF = 1<<29; vector<pair<int,int> > vec[N], val[N]; int n, m, h[N], sta[N], stacnt, incnt, arr[N], arrcnt, sum, mx, f[N], cnt0, rank[N]; struct inter { int l, r, ans; inter *fa; bool operator < (const inter &that) const { return r == that.r ? l < that.l : r > that.r; } }in[N<<1]; bool cmp_len(int a, int b){return in[a].r-in[a].l+1 < in[b].r-in[b].l+1;} struct BIT { int t[N*10]; int query(int x) { int r = 0; for(; x; x -= lowbit(x)) { in[r].l < in[t[x]].l ? r = t[x] : 0; } return r; } void modi(int x, int v) { for(; x < N; x += lowbit(x)) { if(in[v].r < in[t[x]].r || (in[v].r == in[t[x]].r && in[t[x]].l < in[v].l)) t[x] = v; } } }T; struct SEG { int t[N*10]; void build(int x, int l, int r) { if(l == r) {t[x] = h[l]; return;} int mid = (l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); t[x] = max(t[x<<1], t[x<<1|1]); } int query(int x, int l, int r, int ql, int qr) { if(ql <= l && r <= qr) return t[x]; int mid = (l+r)>>1, ret = 0; if(ql <= mid) ret = max(ret, query(x<<1,l,mid,ql,qr)); if(mid < qr) ret = max(ret, query(x<<1|1,mid+1,r,ql,qr)); return ret; } }S; struct seg { seg *ch[2]; int sum, mx; }mem[N*20], *tot, *null, *root[N]; seg *newseg() { seg *x = ++tot; *x = *null; return x; } void init() { null = tot = mem; null->ch[0] = null->ch[1] = null; null->sum = null->mx = 0; for(int i = 0; i <= incnt; i++) root[i] = newseg(); } void pushup(seg *x) { x->sum = x->ch[0]->sum + x->ch[1]->sum; x->mx = max(x->ch[0]->mx, x->ch[0]->sum + x->ch[1]->mx); } void insert(seg *x, int l, int r, int p, int v) { if(l == r){x->sum += v; x->mx = max(x->sum, 0); return;} int mid = (l+r)>>1; if(p <= mid) { if(x->ch[0] == null) x->ch[0] = newseg(); insert(x->ch[0], l, mid, p, v); } else { if(x->ch[1] == null) x->ch[1] = newseg(); insert(x->ch[1], mid+1, r, p, v); } pushup(x); } seg *merge(seg *x, seg *y, int l, int r) { if(x == null) return y; if(y == null) return x; seg *p = newseg(); if(l == r) { p->sum = x->sum + y->sum; p->mx = max(0, p->sum); return p; } int mid = (l+r)>>1; p->ch[0] = merge(x->ch[0], y->ch[0], l, mid); p->ch[1] = merge(x->ch[1], y->ch[1], mid+1, r); pushup(p); return p; } int query_sum(seg *x, int l, int r, int ql, int qr) { if(ql <= l && r <= qr) return x->sum; int mid = (l+r)>>1, ret = 0; if(ql <= mid) ret += query_sum(x->ch[0], l, mid, ql, qr); if(mid < qr) ret += query_sum(x->ch[1], mid+1, r, ql, qr); return ret; } void query_mx(seg *x, int l, int r, int ql, int qr) { if(ql <= l && r <= qr) {mx = max(mx, sum + x->mx); sum += x->sum; return;} int mid = (l+r)>>1; if(ql <= mid) query_mx(x->ch[0], l, mid, ql, qr); if(mid < qr) query_mx(x->ch[1], mid+1, r, ql, qr); } void main() { int task; scanf("%d",&task); for(; task--; ) { scanf("%d%d",&n,&m); for(int i = 1; i < n; i++) { scanf("%d",&h[i]); arr[++arrcnt] = h[i]; for(; stacnt && h[sta[stacnt]] <= h[i]; ) { in[++incnt] = (inter){sta[stacnt], i, 0, 0}; stacnt--; } in[++incnt] = (inter){sta[stacnt], i, 0, 0}; sta[++stacnt] = i; } for(; ~stacnt; stacnt--) in[++incnt] = (inter){sta[stacnt], n, 0, 0}; for(int i = 1, p, y, k; i <= m; i++) { scanf("%d%d%d",&p,&y,&k); cnt0 += k == 0; vec[p].push_back(mkp(y,k)); arr[++arrcnt] = y; } arr[++arrcnt] = h[0] = h[n] = INF; arr[++arrcnt] = -1; sort(arr+1, arr+1+arrcnt); arrcnt = unique(arr+1, arr+1+arrcnt) - arr - 1; for(int i = 1; i <= n; i++) for(int j = 0, jj = vec[i].size(); j < jj; j++) vec[i][j].first = lower_bound(arr+1, arr+1+arrcnt, vec[i][j].first) - arr; for(int i = 0; i <= n; i++) h[i] = lower_bound(arr+1, arr+1+arrcnt, h[i]) - arr; sort(in+1, in+1+incnt); in[0].l = -INF; in[0].r = INF; for(int i = 1; i <= incnt; i++) { in[i].fa = &in[T.query(in[i].l+1)]; T.modi(in[i].l+1, i); } for(int i = 1; i <= incnt; i++) rank[i] = i; sort(rank+1, rank+1+incnt, cmp_len); init(); S.build(1,0,n); for(int ii = 1, i; ii <= incnt; ii++) { i = rank[ii]; int d, u = min(h[in[i].l], h[in[i].r]); if(in[i].l + 1 == in[i].r) { for(int j = 0, jj = vec[in[i].r].size(); j < jj; j++) { insert(root[i], 1, arrcnt, vec[in[i].r][j].first, vec[in[i].r][j].second == 1 ? 1 : -1); } d = 1; } else d = S.query(1,0,n,in[i].l+1, in[i].r-1); sum = 1 < d ? query_sum(root[i], 1, arrcnt, 1, d-1) : 0; mx = 0; query_mx(root[i], 1, arrcnt, d, u-1); in[i].ans = mx; val[in[i].r].push_back(mkp(in[i].l, mx)); if(in[i].fa != &in[0]) root[in[i].fa-in] = merge(root[in[i].fa-in], root[i], 1, arrcnt); } for(int i = 1; i <= n; i++) for(int j = 0, jj = val[i].size(); j < jj; j++) f[i] = max(f[i], f[val[i][j].first] + val[i][j].second); printf("%d\n",f[n]+cnt0); stacnt = arrcnt = incnt = cnt0 = 0; memset(f, 0, sizeof(f)); memset(T.t, 0, sizeof(T.t)); memset(S.t, 0, sizeof(S.t)); for(int i = 0; i <= n; i++) vec[i].clear(), val[i].clear(); } }}int main(){ runzhe2000::main();}
阅读全文
0 0
- LOJ 6032 「雅礼集训 2017 Day2」水箱
- [霍尔定理]「2017 山东一轮集训 Day2」LOJ 6062——PAIR
- loj #6062. 「2017 山东一轮集训 Day2」Pair(线段树)
- LOJ 6041 「雅礼集训 2017 Day7」事情的相似度
- LOJ 6043 「雅礼集训 2017 Day7」蛐蛐国的修墙方案
- [线段树][简单复杂度分析]LOJ#6029. 「雅礼集训 2017 Day1」市场
- LOJ 6045. 「雅礼集训 2017 Day8」价(最大闭合子图)
- 【雅礼集训2017】Day2 棋盘游戏
- [线段树][二分图 霍尔定理]LOJ#6062 && 2017 山东一轮集训 Day2. Pair
- LOJ 6100 「2017 山东二轮集训 Day1」第一题
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- [倍增NTT][DP] LOJ#6059. 「2017 山东一轮集训 Day1」Sum
- [动态网络 网络流] LOJ#6068.「2017 山东一轮集训 Day4」棋盘
- [最短路 杂题] LOJ#6075. 「2017 山东一轮集训 Day6」重建
- [莫队维护DP] LOJ#6074. 「2017 山东一轮集训 Day6」子序列
- [费用流]LOJ#6079. 「2017 山东一轮集训 Day7」养猫
- [后缀自动机 DP] LOJ#6071. 「2017 山东一轮集训 Day5」字符串
- yii2下action下直接返回图片给浏览器
- GoogleTest测试框架
- touch事件笔记
- Linux内核分析
- 用 Pipeline 将训练集参数重复应用到测试集
- LOJ 6032 「雅礼集训 2017 Day2」水箱
- 2017.6.27DI/DO测试总结
- 【JZOJ5167】下蛋爷
- Spring cloud
- Scala练习-直接插入排序
- c语言中全局变量重定义
- JS事件之事件类型[焦点事件]
- Git 从入门到精通(忽略某些文件.gitignore)(五)
- ORACLE 行转列谓词推入