码迷,mamicode.com
首页 > 其他好文 > 详细

hihocoder 1169 猜数字

时间:2015-10-03 15:36:15      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:

传送门

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

描述

你正在和小冰玩一个猜数字的游戏。小冰首先生成一个长为N的整数序列A1, A2, …, AN。在每一轮游戏中,小冰会给出一个区间范围[L, R],然后你要猜一个数K。如果K在AL, AL+1, …, AR中,那么你获胜。

在尝试了几轮之后,你发现这个游戏太难(无聊)了。小冰决定给你一些提示,你每猜一次,小冰会告诉你K与AL, AL+1, …, AR中最接近的数的绝对差值,即min(|Ai - K|), L ≤ i ≤ R。

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

输入

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

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

第二行为N个由空格分开的整数,分别代表A1, A2, …, AN。

接下来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..n],数k区间a[l..r]上的Rank--记作Rank(k, l, r)--定义为
  a[l..r]上小于k的数字的个数
-----------------------------------------------------------
对于题目给出的每组询问L, R, K,先求出Rank(K, L, R),剩下的问题就是求(静态)区间第k小。
为了方便表述,将a[l..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)。
-------------------------------------------------------------------------------------------------------------
Implmentation:
#include <bits/stdc++.h>
using namespace std;

const int N(2e5+5);

int a[19][N], toleft[19][N], sa[N];

void build(int lev, int l, int r){
    if(l==r) return;
    int mid=(l+r)>>1, &tar=sa[mid], nl=mid-l+1;
    for(int i=l; i<=r; i++) if(a[lev][i]<tar) nl--;
    for(int i=l, lp=l, rp=mid+1; i<=r; i++){
        if(a[lev][i]<tar) a[lev+1][lp++]=a[lev][i];
        else if(a[lev][i]>tar) a[lev+1][rp++]=a[lev][i];
        else nl?a[lev+1][lp++]=a[lev][i],nl--:a[lev+1][rp++]=a[lev][i];
        toleft[lev][i]=toleft[lev][l-1]+lp-l; 
    }
    build(lev+1, l, mid); build(lev+1, mid+1, 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-1], nr=r-l+1-nl, mid=(L+R)>>1;

    if(sa[mid]>=val){
        if(nl){
             l=L+toleft[lev][l-1]-toleft[lev][L-1];
            r=l+nl-1;
            return Rank(lev+1, L, mid, l, r, val);
        }
        return 0;    //error-prone
    }
    else{
        if(nr){
            r+=toleft[lev][R]-toleft[lev][r];
            l=r-nr+1;
            return nl+Rank(lev+1, mid+1, 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-1], nr=r-l+1-nl, mid=(L+R)>>1;

    if(nl>=k){
        l=L+toleft[lev][l-1]-toleft[lev][L-1];
        r=l+nl-1;
        return Query(lev+1, L, mid, l, r, k);
    }
    r+=toleft[lev][R]-toleft[lev][r];
    l=r-nr+1;
    return Query(lev+1, mid+1, R, l, r, k-nl);
}


int main(){
    int T; scanf("%d", &T);
    for(int n, q, cs=0; T--;){
        scanf("%d%d", &n, &q);
        printf("Case #%d:\n", ++cs);

        for(int i=1; i<=n; i++) scanf("%d", sa+i), a[0][i]=sa[i];
        sort(sa+1, sa+n+1);

        build(0, 1, n);

        for(int l, r, k, rk, res; q--;){
            scanf("%d%d%d", &l, &r, &k);
            rk=Rank(0, 1, n, l, r, k);
            //printf("%d\n", rk);
            if(rk==0) res=Query(0, 1, n, l, r, rk+1)-k;
            else if(rk==r-l+1) res=k-Query(0, 1, n, l, r, rk);
            else res=min(Query(0, 1, n, l, r, rk+1)-k, k-Query(0, 1, n, l, r, rk));
            printf("%d\n", res);
        } 
    }
}

--------------------------------------------------------

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

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

---------------------------------------------------------

hihocoder 1169 猜数字

标签:

原文地址:http://www.cnblogs.com/Patt/p/4853401.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!