传送门

时间限制: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. Android Activity的生命周期

    一.为什么要了解Activity的生命周期 activity is directly affected by its association withother activities, its tas ...

  2. iOS app上传错误集锦(转载)

    1.工程里增加了版本自动更新. 2.未增加判断网络状态的类Reachability. 3.问题:error itms -90049 This bundel is invalid. The bundle ...

  3. 装了个干净的win7

    lanny的电脑基本信息 我的电脑 联想 ThinkPad T450s 笔记本电脑 操作系统 Windows 旗舰版 64位 主显卡 集成显卡 IE浏览器 版本号 8.0 基本硬件展示 处理器 英特尔 ...

  4. timeSeries db之:使用Metrics监控应用程序的性能 (zz)

    在编写应用程序的时候,通常会记录日志以便事后分析,在很多情况下是产生了问题之后,再去查看日志,是一种事后的静态分析.在很多时候,我们可能需要了解整个系统在当前,或者某一时刻运行的情况,比如当前系统中对 ...

  5. MVC5 + EF6 + Bootstrap3 (9) HtmlHelper用法大全(下)

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc5-ef6-bs3-get-started-httphelper-part2.html 上一节 ...

  6. JS判断输入是否为整数和数字的正则表达式

    "^\\d+$" //非负整数(正整数 + 0) "^[0-9]*[1-9][0-9]*$" //正整数 "^((-\\d+)|(0+))$" ...

  7. [MCSM]Exponential family: 指数分布族

    Exponential family(指数分布族)是一个经常出现的概念,但是对其定义并不是特别的清晰,今天好好看了看WIKI上的内容,有了一个大致的了解,先和大家分享下.本文基本是WIKI上部分内容的 ...

  8. C#Json序列化和反序列化

    1.动态决定数据是否要序列化 我的需求是这样的,我用了一款数据库的组件叫Dos.ORM,确实方便了不少,但是在用的时候,我发现一个问题,比如我定义的表中有一个字段添加时间,修改时间,这些都是默认的,在 ...

  9. groot 引入外部模板

    index7.html <html><head> <title>groots引入外部模板van</title> <script src=" ...

  10. 关于#define预处理指令的一个问题

    背景:由于经常需要在远程服务端和测试服务端进行切换,所以将接口的地址定义为了一个预处理变量,例如 //#define APIDOMAIN @"http://10.0.0.2" #d ...