[ARC082F] Sandglass(线段树)
Description
有一个沙漏由两个上下相通玻璃球 \(A\) 和 \(B\) 构成,这两个玻璃球都含有一定量的沙子,我们暂且假定 \(AB\) 中位于上方的玻璃球的为 \(U\),下方的玻璃球为 \(L\),则除非 \(U\) 中没有沙子,否则每秒钟都会有1克沙子从 \(U\) 掉入 \(L\)。
在第 \(0\) 个时刻,\(A\) 中有 \(a\) 克沙子,\(B\) 中有 \(X−a\) 克沙子(总共有 \(X\) 克沙子),且 \(U\) 为 \(A\),\(L\) 为 \(B\) (即 \(A\) 上 \(B\) 下)。
在 \(r1,r2,...,rK\) 这些时刻,我们将倒转整个沙漏,使得原来的 \(U\) 变成 \(L\),原来的 \(L\) 变成 \(U\)。对于翻转操作,t时刻是指从第 \(0\) 个时刻起经过 \(t\) 秒后的时刻,我们可以将翻转沙漏的操作看做瞬间完成的。
现在有 \(Q\) 次询问,每一次询问会给定一对非负整数(\(t_i\),\(a_i\)),求 \(a=a_i\),第 \(t_i\) 时刻,\(A\) 中所含沙子的克数。
Input
第一行一个正整数X
第二行一个正整数K
第三行K个整数,表示r1,r2,...,rK
接下来一行一个正整数Q
接下来Q行,每行两个非负整数,分别表示每次次询问的(ti,ai)
Output
一共Q行
对于每次询问,输出一行一个非负整数表示答案。
题解:
神题啊!
这题可以用线段树做,我们维护 1~X 中,每个 a 经过一系列操作后的值,我们只用求每个询问中的 a,为了避免爆空间,我们动态开点。
我们把询问按时间排序,同时操作 r 数组,如果A再上面就减区间长度,和0取max,不然就加,与X取min。
像这样:
for(int i=0,j=1,rev=0;i<=n;i++,rev^=1){
while(j<=m&&q[j].t<=r[i+1]){
int tmp=query(root,1,X+1,q[j].a);
if(rev)tmp=min(X,tmp+q[j].t-r[i]);
else tmp=max(0,tmp-q[j].t+r[i]);
ans[q[j++].id]=tmp;
}
if(j>m)break;
int tmp=r[i+1]-r[i];
if(rev)Add(root,1,X+1,tmp);
else Sub(root,1,X+1,tmp);
}
然后每个线段树节点维护当前区间的最小值和最大值。
加的话,这样:
if(minv[o]>=X-x)
maxv[o]=minv[o]=set[o]=X,add[o]=0;
else if(maxv[o]<=X-x)
maxv[o]+=x,minv[o]+=x,add[o]+=x;
减就这样:
if(maxv[o]<=x)
maxv[o]=minv[o]=add[o]=set[o]=0;
else if(minv[o]>=x)
maxv[o]-=x,minv[o]-=x,add[o]-=x;
思路还是很简单的,这题主要还是码码。
COMPLETE CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,X,tot=0,root,r[100005];
int lch[8000005],rch[8000005];
int minv[8000005],maxv[8000005];
int set[8000005],add[8000005];
struct Question{
int t,a,id;
}q[100005];
int ans[100005];
bool comp(Question a,Question b){return a.t<b.t;}
int newnode(int &o,int x,int y){
o=++tot,minv[o]=x,maxv[o]=y,set[o]=-1;
}
void pushup(int o){
minv[o]=min(minv[lch[o]],minv[rch[o]]);
maxv[o]=max(maxv[lch[o]],maxv[rch[o]]);
}
void pushdown(int o,int l,int r){
int mid=l+r>>1;
int &lc=lch[o],&rc=rch[o];
if(!lc)newnode(lc,l,mid-1);
if(!rc)newnode(rc,mid,r-1);
if(~set[o]){
minv[lc]=maxv[lc]=set[lc]=set[o];
minv[rc]=maxv[rc]=set[rc]=set[o];
add[lc]=add[rc]=0,set[o]=-1;
}
if(add[o]){
minv[lc]+=add[o],maxv[lc]+=add[o],add[lc]+=add[o];
minv[rc]+=add[o],maxv[rc]+=add[o],add[rc]+=add[o];
add[o]=0;
}
}
void Sub(int &o,int l,int r,int x){
if(o==0)newnode(o,l,r-1);
if(maxv[o]<=x)
maxv[o]=minv[o]=add[o]=set[o]=0;
else if(minv[o]>=x)
maxv[o]-=x,minv[o]-=x,add[o]-=x;
else{
pushdown(o,l,r);
int mid=l+r>>1;
Sub(lch[o],l,mid,x),Sub(rch[o],mid,r,x);
pushup(o);
}
}
void Add(int &o,int l,int r,int x){
if(o==0)newnode(o,l,r-1);
if(minv[o]>=X-x)
maxv[o]=minv[o]=set[o]=X,add[o]=0;
else if(maxv[o]<=X-x)
maxv[o]+=x,minv[o]+=x,add[o]+=x;
else{
pushdown(o,l,r);
int mid=l+r>>1;
Add(lch[o],l,mid,x),Add(rch[o],mid,r,x);
pushup(o);
}
}
int query(int o,int l,int r,int x){
if(o==0)newnode(o,l,r-1);
if(r-l==1)return minv[o];
pushdown(o,l,r);
int mid=l+r>>1;
if(x<mid)return query(lch[o],l,mid,x);
else return query(rch[o],mid,r,x);
}
int main(){
scanf("%d%d",&X,&n);
for(int i=1;i<=n;i++)scanf("%d",r+i);
r[n+1]=1e9;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].t,&q[i].a);
q[i].id=i;
}
sort(q+1,q+m+1,comp);
for(int i=0,j=1,rev=0;i<=n;i++,rev^=1){
while(j<=m&&q[j].t<=r[i+1]){
int tmp=query(root,1,X+1,q[j].a);
if(rev)tmp=min(X,tmp+q[j].t-r[i]);
else tmp=max(0,tmp-q[j].t+r[i]);
ans[q[j++].id]=tmp;
}
if(j>m)break;
int tmp=r[i+1]-r[i];
if(rev)Add(root,1,X+1,tmp);
else Sub(root,1,X+1,tmp);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
[ARC082F] Sandglass(线段树)的更多相关文章
- bzoj3932--可持久化线段树
题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...
- codevs 1082 线段树练习 3(区间维护)
codevs 1082 线段树练习 3 时间限制: 3 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...
- codevs 1576 最长上升子序列的线段树优化
题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...
- codevs 1080 线段树点修改
先来介绍一下线段树. 线段树是一个把线段,或者说一个区间储存在二叉树中.如图所示的就是一棵线段树,它维护一个区间的和. 蓝色数字的是线段树的节点在数组中的位置,它表示的区间已经在图上标出,它的值就是这 ...
- codevs 1082 线段树区间求和
codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...
- PYOJ 44. 【HNSDFZ2016 #6】可持久化线段树
#44. [HNSDFZ2016 #6]可持久化线段树 统计 描述 提交 自定义测试 题目描述 现有一序列 AA.您需要写一棵可持久化线段树,以实现如下操作: A v p x:对于版本v的序列,给 A ...
- CF719E(线段树+矩阵快速幂)
题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...
- 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 224 Solved: 95[Submit][Status][Discuss] ...
- 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集
3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 1878 Solved: 846[Submit][Status ...
随机推荐
- 1072: [SCOI2007]排列perm
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3000 Solved: 1875[Submit][Status][Discuss] Descript ...
- 2018.10.30 NOIp模拟赛T2 数字对
[题目描述] 小 H 是个善于思考的学生,现在她又在思考一个有关序列的问题. 她的面前浮现出一个长度为 n 的序列{ai},她想找出一段区间[L, R](1 <= L <= ...
- 分享一个漂亮按钮插件FancyButtons
一转眼,2018年的第10天就这样过去了.回看17年,曾经做了些啥都忘记了,就像每一天写日志时的样子(双手放在键盘上,怒着嘴,抬着头,望着天花板), 然后突然记得好像好久没有写随笔了(@_@).自从配 ...
- php实现的三个常用加密解密功能函数示例
目录 算法一: 算法二: 算法三(改进第一个加密之后的算法) 本文实例讲述了php实现的三个常用加密解密功能函数.分享给大家供大家参考,具体如下: 算法一: //加密函数 function lock_ ...
- 日志收集系统Flume及其应用
Apache Flume概述 Flume 是 Cloudera 提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统.Flume 支持定制各类数据发送方,用于收集各类型数据:同时,Fl ...
- vim 命令总结
命令历史 以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择某个历史命令. 启动vim 在命令行窗口中输入以下命令即可 vim 直接启动vim vim filename 打开vim ...
- HTTP认证之摘要认证——Digest(一)
导航 HTTP认证之基本认证--Basic(一) HTTP认证之基本认证--Basic(二) HTTP认证之摘要认证--Digest(一) HTTP认证之摘要认证--Digest(二) 一.概述 Di ...
- play后面加the不加the如何分辨
play表示“参加(某种球类运动或棋牌类的活动)”时,不需要定冠词the,后面直接加球类运动名称或棋牌类活动名称,可根据实际情况翻译成“打,踢,下”等.例如: 1) He often plays fo ...
- windows下git安装以及基本配置
一.首先要下载git然后进行默认安装即可,然后查看系统环境变量是否添加 二.桌面右击鼠标,选择git bash 进入操作页面 git --version 检测是否安装成功 安装完成有配置一 ...
- IOS开发学习笔记036-UIScrollView-循环自动滚动
实现scrollView的自动循环滚动,需要实现几个方法. 其中scrollView中始终保存三张图片,其他的图片在滚动到时再进行加载. 循环的实现主要是在setUpdate 中,如果索引为0是第一个 ...