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. Java传值分析

    public class Example{String str=new String("good");char[] ch={'a','b','c'};public static v ...

  2. Linux NFS服务器的安装与配置详解

    一.NFS服务简介 NFS是Network File System(网络文件系统).主要功能是通过网络让不同的服务器之间可以共享文件或者目录.NFS客户端一般是应用服务器(比如web,负载均衡等),可 ...

  3. Thinkphp5封装上传图片

    <?php namespace app\api\controller; use think\Controller; use think\Db; class Plus extends Base{ ...

  4. 用Python学分析 - 单因素方差分析

    单因素方差分析(One-Way Analysis of Variance) 判断控制变量是否对观测变量产生了显著影响 分析步骤 1. 建立检验假设 - H0:不同因子水平间的均值无差异 - H1:不同 ...

  5. 【Python】剑指offer 14:剪绳子

    题目:给你一根长度为n的绳子,请把绳子剪成m段 (m和n都是整数,n>1并且m>1)每段绳子的长度记为k[0],k[1],-,k[m].请问k[0]k[1]-*k[m]可能的最大乘积是多少 ...

  6. mem_init()

    原本由bootmem管理的内存在mem_init函数中交由伙伴系统管理. 1.free_unused_memmap_node 相邻的membank间可能存在空洞,但在bootmem阶段这些空洞页也分配 ...

  7. 二分法:CF371C-Hamburgers(二分法+字符串的处理)

    Hamburgers Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u    Desc ...

  8. Birthday Paradox

    Birthday Paradox Sometimes some mathematical results are hard to believe. One of the common problems ...

  9. OpenCV学习笔记(十) 直方图操作

    直方图计算 直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等).直方图的一些具体细节: dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅 ...

  10. optparser 模块 提取IP,端口,用户名,密码参数模板

    import optparse #class FtpClient(object): #自定义类可以自己修改 '''ftp客户端''' #def __init__(self): parser = opt ...