CodeForces Optimal Subsequences (Hard Version)

题目链接:传送门

题意简述 

这道题有两个版本,简单版本只需要简单的模拟就可以了。困难版则是将数据量加大到2E5的级别。所以需要提高代码的执行效率才能通过。

题目给定一个长度为N的序列,目的是求出符合以下要求的子序列:

  1. 长度为K
  2. 子序列元素总和最大
  3. 子序列在符合上面的条件的同时需要字典序最小

题目的考察方式是给定K和一个Pos,要求你返回长度为K的上述子序列中第pos个元素。并且给定的(K,pos)对的数量级也是2E5。

题意分析

通过题意可以看出对代码的执行效率要求比较高。N^2的复杂度显然过不了。所以我们将目标转向NlogN。然后整个解题思路如下:

  1. 贪心构造出子序列
  2. 找出长度为K的子序列的第pos个元素

题目的查询的数量级比较大,并且每个查询的长度K和pos也不一定是按一定的大小顺序给出。所以我们需要离线所有的查询,并且将查询按照某种顺序排列好,比如按K的大小从大到小排列好, 这样我们首先构造出最小的子序列,后面的序列可以在前面的基础上添加一定的元素构造出来。

这时又遇到一个问题,我们很容易想到利用贪心思想将原始序列按一定的规则排序后可以很简单的构造出符合要求的特定长度的的子序列,但是我们对构造出来的子元素无法做到随机访问(个人认为实现这个目标所执行的步骤已经超时了),而顺序访问显然会超时。所以我们需要在这个找第pos个元素上花功夫。也就是减少该步骤的复杂度。

解题方法

我采用的方法是利用树状数组来加速查询的操作,具体看代码:

我利用树状数组维护信息是:为构造子序列,我从前i个原始序列元素中选取的元素的数量,记为S[i]。维护了这个信息之后,如果我想询问第pos个元素是哪个,只需要二分查询出满足S[i]==pos的最小i即可。则第i个原始元素便是子序列第pos个元素。

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <math.h>
#include <queue>
#include <string>
#include <sstream>
#include <vector>
#include <string.h>
#include <time.h>
using namespace std;
 
const int maxn = 2e5 + 5;
const int INF = 1e9;
 
struct Data{
    int a,idx;
 
    bool operator < (const Data & b) const{
        return a == b.a ? idx < b.idx : a > b.a;
    }
} d[maxn];
 
struct Query{
    int k,pos,idx;
 
    bool operator < (const Query & b) const{
        return k < b.k;
    }
}q[maxn];
 
 
int res[maxn];
 
int n;
 
inline int lowbit(int x){
    return x&(-x);
}
 
int C[maxn];
int b[maxn];
 
inline void update(int idx, int v){
    for(;idx <= n; idx+= lowbit(idx)){
        C[idx] += v;
    }
}
 
 
inline int query(int idx){
    int sum = 0;
    for(; idx; idx -= lowbit(idx)){
        sum += C[idx];
    }
    return sum;
}
 
inline int solve() {
 
    memset(C, 0, sizeof(C));
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &d[i].a);
        d[i].idx = i;
        b[i] = d[i].a;
    }
    sort(d+1, d+1+n);
    int m;
    scanf("%d", &m);
    for(int i = 0; i < m; i++){
        scanf("%d %d", &q[i].k,&q[i].pos);
        q[i].idx = i;
    }
    sort(q, q+m);
    int curL = 1;
    for(int i = 0; i < m; i++){
        for(; curL <= q[i].k; curL++){
            update(d[curL].idx, 1);
        }
        int l = 1, r = n;
        while(l < r){
            int mid = l + r >> 1;
            if(query(mid) >= q[i].pos){
                r = mid;
            }else{
                l = mid + 1;
            }
        }
        res[q[i].idx] = b[l];
    }
    for(int i = 0; i < m; i++){
        printf("%d\n", res[i]);
    }
 
    return 0;
}
 
 
int main() {
    int t = 1;
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
//    freopen("1.out", "w", stdout);
    int mao = clock();
    scanf("%d", &t);
#endif
    while(t--) {
        solve();
    }
#ifndef ONLINE_JUDGE
    cerr << "Time:" << clock() - mao << "ms" << endl;
#endif
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章