雅礼集训 2017 Day2 水箱 可并堆
题目描述
给出一个长度为 n 宽度为 1 ,高度无限的水箱,有 n−1 个挡板将其分为 n 个 1 - 1 的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律的(在无挡板阻拦的情况下会向低处流),现在有 m 个条件 (i,y,k) ,表示从左到右数的第 i 个格子中,在高度为 y+0.5 的地方是否有水,k=1 表示有水,k=0 表示没有水,请求出这 m 个条件最多能同时满足多少个条件。本题有多组数据。
输入格式
第一行一个正整数 T ,为数据组数。
第二行两个正整数 n 、m ,中间用空格隔开。
接下来一行 n−1 个整数,表示从左到右每一块隔板的高度。
接下来 m 行,每行三个整数 i 、y 、k ,表示一个条件。
输出格式
共 T 行,每行对应一组数据的答案。
样例
样例输入
2
3 4
3 4
1 3 1
2 1 0
2 2 0
3 3 1
2 2
2
1 2 0
1 2 1
样例输出
3
1
数据范围与提示
对于 20% 的数据,n,m≤16;
对于另外 10% 的数据,只存在指明某处有水的条件;
对于另外 30% 的数据,n,m≤1000;
对于 100% 的数据,n,m≤1e5,T≤5;
看到这道题,首先应该想到最高的隔板把整个区间分割成两个独立的部分,所以对于整段的答案,可以是左右两个部分的独立的答案(即水没有淹没最高的那个隔板),或者先把两个部分淹没,让水没过最高的隔板,然后在两侧最近的更高的隔板(箱壁可视为无限高的隔板)的高度以下讨论答案。
我用的是线段树维护区间最高挡板位置,用可并堆维护区间所有要求。
初始时,对于每一个单位水槽,把它上方所有要求按高度维护小根堆,合并时就先直接合并它左右两个部分,再将堆中左右小于两侧隔板高度的要求按照顺序提出来,枚举淹没高度,用前缀有水条件个数加后缀无水条件个数更新答案,设这个答案最大值为sum。
对于区间[L,R],若L=R,ans(L,R)=sum。
否则,[L,R]一定能从最高的隔板处分为[L,k],[k+1,R],两个部分,
可以用ans(L,k)+ans(k+1,R)+高于第k和第k+1之间的隔板低于L外侧隔板且低于R外侧隔板间所有无水条件的个数更新答案,
也可以用所有[L,R]之间低于第k和第k+1之间的隔板的有水条件个数+sum更新答案。
是的有点像分治...
复杂度分析的话,每个隔板可以和左侧比它高的第一个隔板和右侧比它高的第一个隔板分别形成两个区间,所以大概有2n个区间吧...然后每个要求在可并堆中共计被访问一次,删除一次,所以再加一个mlogm,肯定是能过的。
AC代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200020
#define mid (l+r>>1)
using namespace std;
int read(){
int nm=0,fh=1;char cw=getchar();
for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return nm*fh;
}
struct req{int pos,hs,tpe;}p[M];
bool cmp(req i,req j){
if(i.pos==j.pos) return i.hs<j.hs;
return i.pos<j.pos;
}
int n,m,T,tmp,L[M],R[M],c[M<<2],t[M],ans,rt[M<<2],f[M<<2];
int tot[M<<2],dst[M<<2],son[M][2],fs[M],gt[M],pre[M],suf[M],tk;
void build(int x,int l,int r){
if(l==r){c[x]=l;return;}
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
if(t[c[x<<1]]>t[c[x<<1|1]]) c[x]=c[x<<1];
else c[x]=c[x<<1|1];
}
int top(int x,int l,int r,int lt,int rt){
if(rt<l||r<lt) return lt;
if(lt<=l&&r<=rt) return c[x];
int t1=top(x<<1,l,mid,lt,rt),t2=top(x<<1|1,mid+1,r,lt,rt);
return t[t1]>t[t2]?t1:t2;
}
int merge(int x,int y){
if(x*y==0) return x+y;
if(p[x].hs>p[y].hs) return merge(y,x);
int t1=son[x][0],t2=son[x][1],k=merge(t2,y);
son[x][1]=k;
if(dst[k]>dst[t1]) swap(k,t1);
dst[x]=dst[k]+1,son[x][0]=t1,son[x][1]=k;
return x;
}
int del(int x){
int t1=son[x][0],t2=son[x][1];
son[x][0]=son[x][1]=dst[x]=0;
return merge(t1,t2);
}
void dp(int &x,int l,int r){
x=++tmp,f[x]=tot[x]=0;
int cnt=0,rf=min(t[l-1],t[r]),sum=0;
if(l==r) rt[x]=fs[l];
else{
int tp=top(1,1,n-1,l,r-1);
dp(L[x],l,tp),dp(R[x],tp+1,r);
rt[x]=merge(rt[L[x]],rt[R[x]]);
tot[x]=tot[L[x]]+tot[R[x]];
}
while(rt[x]>0){
if(p[rt[x]].hs>=rf) break;
if(p[rt[x]].tpe==1) tot[x]++;
gt[++cnt]=rt[x],rt[x]=del(rt[x]);
}
suf[cnt+1]=0,pre[0]=0;
for(int i=1;i<=cnt;i++){
pre[i]=pre[i-1],suf[cnt-i+1]=suf[cnt-i+2];
if(p[gt[i]].tpe==1) pre[i]++;
if(p[gt[cnt-i+1]].tpe==0) suf[cnt-i+1]++;
}
for(int i=0;i<=cnt;i++){
if(p[gt[i]].hs==p[gt[i+1]].hs&&i!=0) continue;
sum=max(sum,pre[i]+suf[i+1]);
}
if(l==r){f[x]=sum;return;}
f[x]=max(f[L[x]]+f[R[x]]+suf[1],tot[L[x]]+tot[R[x]]+sum);
}
int main(){
T=read();
while(T--){
n=read(),m=read(),tmp=ans=tk=0,t[0]=t[n]=2147483646;
memset(fs,0,sizeof(fs));
for(int i=1;i<n;i++) t[i]=read();
for(int i=1;i<=m;i++) p[i].pos=read(),p[i].hs=read(),p[i].tpe=read();
build(1,1,n-1),sort(p+1,p+m+1,cmp);
for(int i=1;i<=m;i++){fs[p[i].pos]=merge(i,fs[p[i].pos]);}
dp(tk,1,n),printf("%d\n",f[tk]);
}
return 0;
}
雅礼集训 2017 Day2 水箱 可并堆的更多相关文章
- 「雅礼集训 2017 Day2」解题报告
「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...
- #6034. 「雅礼集训 2017 Day2」线段游戏 李超树
#6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统 ...
- 【loj6034】「雅礼集训 2017 Day2」线段游戏
#6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:Special Judge 上传者: 匿名 题目描述 ...
- loj #6032. 「雅礼集训 2017 Day2」水箱 线段树优化DP转移
$ \color{#0066ff}{ 题目描述 }$ 给出一个长度为 \(n\) 宽度为 \(1\) ,高度无限的水箱,有 \(n-1\) 个挡板将其分为 \(n\) 个 \(1 - 1\) 的小格, ...
- 「雅礼集训 2017 Day2」水箱 (数据结构+dp ,一个log)
题面 题解 在网上看到有些做法,有什么平衡树.启发式合并等等总之复杂度O(Tnlog^2(n))的不优做法,这里我就用一个O(Tnlogn)的做法好了 其实大体上推导的思路都是一样的. 我们很容易发现 ...
- loj#6032. 「雅礼集训 2017 Day2」水箱(并查集 贪心 扫描线)
题意 链接 Sol 神仙题+神仙做法%%%%%%%% 我再来复述一遍.. 首先按照\(y\)坐标排序,然后维护一个扫描线从低处往高处考虑. 一个连通块的内状态使用两个变量即可维护\(ans\)表示联通 ...
- LOJ#6032. 「雅礼集训 2017 Day2」水箱
传送门 首先可以有一个平方复杂度的 \(DP\) 设 \(f_{i,j}\) 表示前面 \(i\) 个小格,高度为 \(j\) 的最大答案 令 \(h_i\) 表示隔板 \(i\) 的高度 当 \(j ...
- 「雅礼集训 2017 Day2」水箱
题目链接 题意分析 我们用\(f[i][j]\)表示当前到达第\(i\)个位置水位高度为\(j\)的答案 如果那么\(h[i]\)为\(i\)和\(i+1\)之间的支柱高度 那么如果\(j≤h[i]\ ...
- loj#6033. 「雅礼集训 2017 Day2」棋盘游戏(二分图博弈)
题意 链接 Sol 第一次做在二分图上博弈的题..感觉思路真是清奇.. 首先将图黑白染色. 对于某个点,若它一定在最大匹配上,那么Bob必胜.因为Bob可以一直沿着匹配边都,Alice只能走非匹配边. ...
随机推荐
- iOS设备获取总结
1.获取iOS设备的各种信息 // 这个方法后面会列出来 NSString *deviceName = [self getDeviceName]; NSLog(@"设备型号-->%@& ...
- Channel (Java NIO)
[正文]netty源码死磕1.3: Java NIO Channel 1. Java NIO Channel 1.1. Java NIO Channel的特点 和老的OIO相比,通道和NIO流(非阻 ...
- Convex combination
en.wikipedia.org/wiki/Convex_combination 凸组合 In convex geometry, a convex combination is a linear co ...
- 教你管理SQL备份与恢复系列(1-20)
原链接:https://bbs.51cto.com/thread-1147908-1.html 教你备份与恢复数据库,直接下面下文档吧. 教你备份与恢复数据库(1)事务 http://bbs.51ct ...
- lzugis—搭建属于自己的小型的版本号控制SVN
版权声明:本文为LZUGIS原创文章,未经同意不得转载. https://blog.csdn.net/GISShiXiSheng/article/details/28643575 对于不了解SVN的同 ...
- 内存写越界导致破环堆结构引起的崩溃问题定位经验[如报错malloc(): memory corruption或free(): invalid next size]
前段时间开发的一个后端C模块上线后,线上出core,初始时,因为訪问压力不大,所以崩溃是上线3天左右出现的.当时用gdb跟进调用堆栈并检查源代码,发现出core位置的代码沒有啥问题.因为当时开发任务较 ...
- STM32 ~ J-LINK V8 修复
1.1 安装固件烧录软件 ♦请ATMEL官方网址下载AT91-ISP下载软件. 软件下载地址:http://www.atmel.com/dyn/products/tools_card.asp?t ...
- 阿里云 rails nginx 配置https访问
1.申请免费型dv ssl证书:https://common-buy.aliyun.com/?spm=a2c4e.11155515.0.0.7zzvOZ&commodityCode=cas#/ ...
- J.U.C重入锁
ReentrantLock重入锁 ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式, 重入的意思就是,如果已经获得了锁,如果执行期间还需要获得这个锁的话,会直接获得 ...
- PAT 天梯赛 L2-009. 抢红包 【排序】
题目链接 https://www.patest.cn/contests/gplt/L2-009 思路 用结构体存储,然后结构体排序 注意一下 个人编号是从 1 开始 计数的 AC代码 #include ...