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(线段树)的更多相关文章

  1. bzoj3932--可持久化线段树

    题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...

  2. codevs 1082 线段树练习 3(区间维护)

    codevs 1082 线段树练习 3  时间限制: 3 s  空间限制: 128000 KB  题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...

  3. codevs 1576 最长上升子序列的线段树优化

    题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...

  4. codevs 1080 线段树点修改

    先来介绍一下线段树. 线段树是一个把线段,或者说一个区间储存在二叉树中.如图所示的就是一棵线段树,它维护一个区间的和. 蓝色数字的是线段树的节点在数组中的位置,它表示的区间已经在图上标出,它的值就是这 ...

  5. codevs 1082 线段树区间求和

    codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...

  6. PYOJ 44. 【HNSDFZ2016 #6】可持久化线段树

    #44. [HNSDFZ2016 #6]可持久化线段树 统计 描述 提交 自定义测试 题目描述 现有一序列 AA.您需要写一棵可持久化线段树,以实现如下操作: A v p x:对于版本v的序列,给 A ...

  7. CF719E(线段树+矩阵快速幂)

    题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...

  8. 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

    3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] ...

  9. 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

随机推荐

  1. js中的||、&&与!用法

    &&和||在JQuery源代码内尤为使用广泛,由网上找了些例子作为参考,对其用法研究了一下: 1. && function a(){ alert("a&quo ...

  2. HH的项链题解(离线思想+链表+树状数组)

    本人第一篇博客重磅推出!!! 希望各位朋友以后多多捧场也多给写意见(我个人喜欢把题解写得啰嗦一点,因为这样方便理解,各位巨佬勿喷) 来讲一道提高+/省选-的骚题:HH的项链(这个HH你理解成皇后呵呵哈 ...

  3. C/C++程序基础 (六)面向对象

    类的特性 抽象.封装.继承.多态.重载 class 和 struct 区别 C中struct仅仅可以包含数据成员,不可以有成员函数,属于复杂数据结构. C++ 中struct成员访问权限默认为publ ...

  4. MySql主从同步笔记

    1.MySql主从同步是基于二进制日志实现的,二进制日志记录了主服务器数据库的所有变动,从服务器通过读取和执行该日志文件保持和主数据库的数据一致: 2.配置主服务器 a.开启二进制日志,找到MySql ...

  5. Mysql主从数据同步cheksum问题

    做主从同步时出现问题,show slave status显示错误: Last_IO_Error: Got fatal error from master when reading data from ...

  6. Table 分页处理

    介绍两种table分页处理:PHP分页 和 js(jquery.table)分页. 一:jquery.table: 1:下载两个文件:table_jui.css 和 jquery.dataTables ...

  7. 【Directory】文件操作(初识文件操作二)

    上篇我们说了关于文件的创建删除更改可以通过File这个类来完成.对于目录的操作其实File类也可以完成创建删除等相关的操作.用法跟文件的方法大致相同. 那么下面就一起来看一下关于目录相关的用法. 一, ...

  8. 工具类commons-io的Tailer用来监控文件

    一.前言:在Linux下有使用tail命令 在Commons-io中也提供这种方法 二.他采用的是线程方式来监控文件内容的变化 1.Tailer类(采用线程的方式进行文件的内容变法) 2.Tailer ...

  9. 全球征集-如何实现回文SQL的查询

    有个表,以下是创建的SQL: CREATE TABLE [dbo].[SysName]( ,) NOT NULL, ) COLLATE Chinese_PRC_CI_AS NULL, ) COLLAT ...

  10. linux下编译运行TIGL Viewer步骤

    linux下编译运行TIGL Viewer步骤(仅为了正确编译安装的话直接跳到步骤3) 1. linux发行版选择:由于linux发行版众多,不同版本包含的库版本可能存在差别,因此需要选择正确的版本. ...