传送门

时间限制:10000ms
单点时限:5000ms
内存限制:256MB

描述

你正在和小冰玩一个猜数字的游戏。小冰首先生成一个长为N的整数序列 $A_1, A_2, \dots , A_N$。在每一轮游戏中,小冰会给出一个区间范围 $[L, R]$,然后你要猜一个数 $K$。如果 $K$ 在 $A_L, A_{L+1}, \dots , A_R$ 中,那么你获胜。

在尝试了几轮之后,你发现这个游戏太难(无聊)了。小冰决定给你一些提示,你每猜一次,小冰会告诉你 $K$ 与 $A_L, A_{L+1}, \dots, A_R$ 中最接近的数的绝对差值,即 $\min(|A_i - K|), L \le i \le R$。

现在,请你实现这个新功能。

输入

第一行为一个整数 $T$,表示数据组数。

每组数据的第一行为两个整数 $N$ 和 $Q$。

第二行为 $N$ 个由空格分开的整数,分别代表 $A_1, A_2, \dots, A_N$。

接下来 $Q$ 行,每行三个由空格隔开的整数 $L、R、K$。

输出

每组数据的先输出一行"Case #X:",X为测试数据编号。

接下来对每个询问输出一行,每行为一个整数,即为所求的值。

数据范围

1 ≤ T ≤ 20

0 ≤ Ai, K ≤ 109

1 ≤ L ≤ R ≤ N

小数据

1 ≤ N, Q ≤ 1000

大数据

1 ≤ N, Q ≤ 200000

输入数据量较大,推荐使用scanf / BufferedReader等IO方法。

样例输入
1
9 3
1 8 3 4 9 2 7 6 5
1 9 10
3 7 9
5 6 5
样例输出
Case #1:
  1
  0
  3

Solution

先定义一个概念
给定数组 $a_{1},\dots,a_{n}$,数 $k$ 在区间 $[l,r]$ 上的Rank——记作Rank(k, l, r)——定义为
  a[l..r]上小于k的数字的个数
对于每组询问 $L, R, K$,先求出Rank(K, L, R),剩下的问题就是求(静态)区间第 $k$ 小
为了方便表述,将 $a[l \dots r]$ 上第 $k$ 小的数记作 least(k, l, r)ans表示每个查询的答案,分三种情况:
(1) Rank(K, L, R) = 0,       ans = least(Rank(K, L, R)+1, L, R) - K 
(2) Rank(K, L, R) = R-L+1,    ans = K - least(Rank(K, L, R), L, R)
(3) otherwise,           ans = min(least(Rank(K, L, R)+1, L, R) - K, K - least(Rank(K, L, R), L, R))
Rank(K, L, R)与least(K, L, R)都可用划分树实现,单次查询的时间复杂度都是O(log n),而且二者代码非常相似。
划分树的空间复杂度是 $O(n \log n)$。

划分树 (partition tree)

划分树是线段树的一种,用于维护数组 $A[1 \dots n]$。它的每个节点u代表数组 $A$ 的一些元素。
假设内部(即非叶子)节点 $u$ 表示元素是 $u[L..R] \subset A$,定义节点 $u$ 的长度 $u.length=R-L+1$,记 $u$ 的左右儿子分别为 $v, w$,令
\[mid\equiv \frac{L+R}{2}\]
\[v.length=mid-L+1,   w.length=R-mid\]
或者表示成
\[ v = v[L, mid], w=w(mid,R] \] 

Implmentation

#include <bits/stdc++.h>
using namespace std; const int N(2e5+); int a[][N], toleft[][N], sa[N]; void build(int lev, int l, int r){
if(l==r) return;
int mid=(l+r)>>, &tar=sa[mid], nl=mid-l+;
for(int i=l; i<=r; i++) if(a[lev][i]<tar) nl--;
for(int i=l, lp=l, rp=mid+; i<=r; i++){
if(a[lev][i]<tar) a[lev+][lp++]=a[lev][i];
else if(a[lev][i]>tar) a[lev+][rp++]=a[lev][i];
else nl?a[lev+][lp++]=a[lev][i],nl--:a[lev+][rp++]=a[lev][i];
toleft[lev][i]=toleft[lev][l-]+lp-l;
}
build(lev+, l, mid); build(lev+, mid+, r);
}
//Rank(l, r, k):区间[l, r]内比k小的数的个数
//满足区间加法
int Rank(int lev, int L, int R, int l, int r, int val){
if(L==R) return a[lev][L]<val; int nl=toleft[lev][r]-toleft[lev][l-], nr=r-l+-nl, mid=(L+R)>>; if(sa[mid]>=val){
if(nl){
l=L+toleft[lev][l-]-toleft[lev][L-];
r=l+nl-;
return Rank(lev+, L, mid, l, r, val);
}
return ; //error-prone
}
else{
if(nr){
r+=toleft[lev][R]-toleft[lev][r];
l=r-nr+;
return nl+Rank(lev+, mid+, R, l, r, val);
}
return nl;
}
} int Query(int lev, int L, int R, int l, int r, int k){
if(L==R) return a[lev][L]; int nl=toleft[lev][r]-toleft[lev][l-], nr=r-l+-nl, mid=(L+R)>>; if(nl>=k){
l=L+toleft[lev][l-]-toleft[lev][L-];
r=l+nl-;
return Query(lev+, L, mid, l, r, k);
}
r+=toleft[lev][R]-toleft[lev][r];
l=r-nr+;
return Query(lev+, mid+, R, l, r, k-nl);
} int main(){
int T; scanf("%d", &T);
for(int n, q, cs=; T--;){
scanf("%d%d", &n, &q);
printf("Case #%d:\n", ++cs); for(int i=; i<=n; i++) scanf("%d", sa+i), a[][i]=sa[i];
sort(sa+, sa+n+); build(, , n); for(int l, r, k, rk, res; q--;){
scanf("%d%d%d", &l, &r, &k);
rk=Rank(, , n, l, r, k);
//printf("%d\n", rk);
if(rk==) res=Query(, , n, l, r, rk+)-k;
else if(rk==r-l+) res=k-Query(, , n, l, r, rk);
else res=min(Query(, , n, l, r, rk+)-k, k-Query(, , n, l, r, rk));
printf("%d\n", res);
}
}
}

问题解决了,但代码不是可以写得再短一些?

Rank和Query可否合并到一起呢?

hihocoder 1169 猜数字的更多相关文章

  1. C语言猜数字游戏

    猜数字游戏,各式各样的实现方式,我这边提供一个实现方式,希望可以帮到新手. 老程序猿就不要看了,黑呵呵 源代码1 include stdio.h include stdlib.h include ti ...

  2. 不一样的猜数字游戏 — leetcode 375. Guess Number Higher or Lower II

    好久没切 leetcode 的题了,静下心来切了道,这道题比较有意思,和大家分享下. 我把它叫做 "不一样的猜数字游戏",我们先来看看传统的猜数字游戏,Guess Number H ...

  3. java 猜数字游戏

    作用:猜数字游戏.随机产生1个数字(1~10),大了.小了或者成功后给出提示. 语言:java 工具:eclipse 作者:潇洒鸿图 时间:2016.11.10 >>>>> ...

  4. 【原创Android游戏】--猜数字游戏Version 0.1

    想当年高中时经常和小伙伴在纸上或者黑板上或者学习机上玩猜数字的游戏,在当年那个手机等娱乐设备在我们那还不是很普遍的时候是很好的一个消遣的游戏,去年的时候便写了一个Android版的猜数字游戏,只是当时 ...

  5. 【原创Android游戏】--猜数字游戏V1.1 --数据存储,Intent,SimpleAdapter的学习与应用

    --------------------------------------------------------------- V0.1版本 上次做完第一个版本后,发现还有一些漏洞,并且还有一些可以添 ...

  6. python学习笔记 ——python写的猜数字游戏 002

    from sys import exit import random def Arrfor(str): #CONTST = CONTST + 1 artificial = input("请输 ...

  7. Python小游戏之猜数字

    最近师兄师姐毕业,各种酒席,酒席上最常玩的一个游戏就是猜数字,游戏规则如下: 出题人在手机上输入一个0-100之间的数字,其它人轮流猜这个数字,如果你不幸猜中则要罚酒一杯.每次猜数字,出题人都要缩小范 ...

  8. 【Qt】2.4 做一个“猜数字”的游戏

    使用对话框和Qt设计师来实现一个相当简单的小游戏.同时将通过这个程序来看布局的隐藏和显示是如何来影响窗口界面的变化的. 新建一个Qt项目,把Qt Creator默认给的mainwindow.h.mai ...

  9. python写的第一个简单小游戏-猜数字

    #Filename:game1.py guess=10 running=True while running: try: answer=int(raw_input('Guess what i thin ...

随机推荐

  1. 关于eclipse入门开发c/c++文章推荐

    1. 关于编译说明. http://www.ibm.com/developerworks/cn/linux/opensource/os-ecc/ 2. 关于快捷键与代码阅读 http://www.cn ...

  2. <<Effective Java>>之善用组合而不是继承

    使用JAVA这门OO语言,第一要义就是,如果类不是专门设计来用于被继承的就尽量不要使用继承而应该使用组合 从上图2看,我们的类B复写了类A的add喝addALL方法,目的是每次调用的时候,我们就能统计 ...

  3. angular常见坑洞

    由于版本不同,可能有些问题在某些版本下出现,某些版本下不出现. 指令不可平行(v1.3.2): 多个指令不能这样一个接着一个排下去: <div> <directive-one/> ...

  4. Go cron定时任务的用法

    cron是什么 cron的意思就是:计划任务,说白了就是定时任务.我和系统约个时间,你在几点几分几秒或者每隔几分钟跑一个任务(job),就那么简单. cron表达式 cron表达式是一个好东西,这个东 ...

  5. U3D physics总结

    物理系统基于collider, 没有collider的物体不会发生任何主动和被动的物理交互,也不会产生trigger相关消息. 当且仅当A和B都有碰撞体时,两者才有可能发生交互,才有可能产生trigg ...

  6. Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

    摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就 ...

  7. 20145215《Java程序设计》第1周学习总结

    20145215<Java程序设计>第一周学习总结 教材学习内容总结 本周先学习了最基本的java环境的搭建. 首先从官网上下载了jdk(jdk与jre的区别在于如果只需要运行一个开发好的 ...

  8. Thrift搭建分布式微服务(二)

    第二篇 连接池  连接池配置,请前往Thrift搭建分布式微服务(一)  下面要介绍的其实不是单一的连接池,应该说是连接池集合.因为它要管理多个Tcp Socket连接节点,每个服务节点都有设置了自己 ...

  9. flatbuffers 使用问题记录

    1. 命名空间的问题 ----------------------------- namespace 1.0.3 版本包含文件类型前面不需要加命名空间,但是1.1.0 中包含需要在类型前加命名空间 i ...

  10. mac使用笔记

    1.QQ多开 MAC中登录QQ后按CMD+N组合按键即可新打开一个QQ登录窗口 2.关闭左右摇晃鼠标放大 系统偏好设置>辅助功能>显示器,去掉“摇动鼠标以定位”前面的勾即可 3.使用ctr ...