HDU 6040 Hints of sd0061(快速排序)

Hints of sd0061

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 520    Accepted Submission(s): 112


Problem Description
sd0061, the legend of Beihang University ACM-ICPC Team, retired last year leaving a group of noobs. Noobs have no idea how to deal with  m  coming contests.  sd0061 has left a set of hints for them.

There are  n  noobs in the team, the  i -th of which has a rating  ai sd0061 prepares one hint for each contest. The hint for the  j -th contest is a number  bj , which means that the noob with the  (bj+1) -th lowest rating is ordained by  sd0061 for the  j -th contest.

The coach asks  constroy to make a list of contestants.  constroy looks into these hints and finds out:  bi+bjbk  is satisfied if  bibj,   bi<bk  and  bj<bk .

Now, you are in charge of making the list for  constroy.
 

Input
There are multiple test cases (about  10 ).

For each test case:

The first line contains five integers  n,m,A,B,C (1n107,1m100)

The second line contains  m  integers, the  i -th of which is the number  bi  of the  i -th hint.  (0bi<n)

The  n  noobs' ratings are obtained by calling following function  n  times, the  i -th result of which is  ai .

unsigned x = A, y = B, z = C;
unsigned rng61() {
  unsigned t;
  x ^= x << 16;
  x ^= x >> 5;
  x ^= x << 1;
  t = x;
  x = y;
  y = z;
  z = t ^ x ^ y;
  return z;
}
 

Output
For each test case, output " Case # x y1   y2     ym " in one line (without quotes), where  x  indicates the case number starting from  1  and  yi   (1im)  denotes the rating of noob for the  i -th contest of corresponding case.
 

Sample Input
  
  
3 3 1 1 1 0 1 2 2 2 2 2 2 1 1
 

Sample Output
  
  
Case #1: 1 1 202755 Case #2: 405510 405510
 

Source
 

Recommend
liuyiding
 

题目大意:

    给你一个长度为N(1<=N<=1e7)的序列的生成方法,有M个查询,每次询问一个数字k,问第k+1大的是数字是哪个。


解题思路:

    开始以为可以根据这个生成方法找到一些性质什么的,但是用了不少时间完全没有找到,实际上真的没有性质。

    首先从最原始的方法出发,我们可以对这个序列排序,然后直接访问第k+1个元素即可。这样时间复杂度为O(N * log N)显然会TLE。仔细想一下,直接全部排序处理出许多我们不需要知道的信息。查询很少,最多只有100个,但是直接排序得到了所有k大的元素,如果我们能够不处理这些信息就可以减少一些时间复杂度。

     回想快速排序的步骤,每次选择一个元素,把比它小的放它前面,比它大的放它后面,这不就是我们要知道的第k的元素吗。快速排序是对所有元素都执行了这个过程,我们只需要其中的m个,也就只需要执行m次这个过程就可了。

     对于这个过程的实现,C++ STL中有一个库函数,nth_element。关于它的用法如下:


    有了这个函数,我们只需要离线的对查询排序,然后对每个查询的值在不断分割的子区间上调用这个函数(实际上就和快速排序的实现差不多),这样就可有很少冗余度的得到我们所需要的答案。

    这样写复杂度的显然是远小于快排的O(N * log N),详细的复杂度计算引用官方题解:



AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXN=10000000+3;
const int MAXM=100+3;
int N,M,A,B,C;
int b[MAXM],pos[MAXM];//查询,查询的id
unsigned a[MAXN],ans[MAXM];//生成的a数组,答案
unsigned x, y, z;

unsigned rng61()
{
    unsigned t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}

inline bool cmp(int x, int y)
{
    return b[x]<b[y];
}

int main()
{
    int cas=1;
    while(~scanf("%d%d%d%d%d",&N,&M,&A,&B,&C))
    {
        x=A, y=B, z=C;
        for(int i=0;i<M;++i)
        {
            pos[i]=i;
            scanf("%d",&b[i]);
        }
        sort(pos,pos+M,cmp);//把查询排序
        pos[M]=M;//加上一个边界,方便判定是否有重复的查询
        b[M]=N;
        for(int i=0;i<N;++i)//生成序列a
            a[i]=rng61();
        for(int i=M-1;~i;--i)//由于题目保证bi+bj<=bk所以一定倒着划分最快
        {
            if(b[pos[i]]==b[pos[i+1]])//如果两个查询的值相同直接复制答案
            {
                ans[pos[i]]=ans[pos[i+1]];
                continue;
            }
            nth_element(a, a+b[pos[i]], a+b[pos[i+1]]);//进行快排的一部,把第k个元素的元素放到第k个,小的放前面,大的放后面
            ans[pos[i]]=a[b[pos[i]]];//保存答案
        }
        printf("Case #%d:",cas++);
        for(int i=0;i<M;++i)
            printf(" %u",ans[i]);
        putchar('\n');
    }
    
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值