[洛谷P2617] Dynamic Rankings

洛谷题目链接:Dynamic Rankings

题目描述

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

对于每一个询问指令,你必须输出正确的回答。

输入输出格式

输入格式:

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。

第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t

  • Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。

  • C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

输出格式:

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

输入输出样例

输入样例#1:

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

输出样例#1:

3
6

说明

10%的数据中,m,n≤100;

20%的数据中,m,n≤1000;

50%的数据中,m,n≤10000。

对于所有数据,m,n≤100000

请注意常数优化,但写法正常的整体二分和树套树都可以以大约1000ms每个点的时间过。

一句话题意: 给你一个长度为\(n\)的序列,要你求出区间第\(k\)小,有修改操作.

题解: 这道题可以用树套树(trie套trie)/带修主席树/整体二分过,这里我写的是整体二分.

首先我们需要会写不带修改的区间第\(k\)小.

然后我们来考虑如何从不带修改的转换到能带修改的整体二分.

对于每一个询问,显然这个询问是在它之前修改的状态后进行查询的.也就是说,对于第\(x\)个操作,如果它是询问,那么它查询的状态是基于前\(x-1\)次操作的.

然而我们在整体二分的时候只对权值进行了二分,如何保证操作是按时间轴顺序进行的呢?

其实我们可以按时间轴顺序加入操作,也就是从最开始的初始数列到后面的\(q\)次询问,都算到我们的操作中.那么这样,我们在第一次整体二分划分区间的时候,就可以保证在某次操作前面的操作在时间轴上的位置一定是小于这次操作的位置的.

那么我们在递归划分二分的两边的时候,并没有另外破坏这个时间轴的顺序,这样,我们就只需要对每次递归的操作按顺序扫过来,一个个进行修改/查询就可以了.

可以自己好好想一下这个过程.

#include<bits/stdc++.h>
using namespace std;
const int N = 100000+5;

int n, m, c[N], a[N], cnt = 0, ans[N*3], id[N*3], t1[N*3], t2[N*3];

struct options{
    int opt, x, y, k;
    options(int _a = 0, int _b = 0, int _c = 0, int _d = 0){
        opt = _a, x = _b, y = _c, k = _d;
    }
}b[N*3];

int lowbit(int x){ return x&-x; }

void update(int x, int val){ for(;x<=n;x+=lowbit(x)) c[x] += val; }

int query(int x){
    int res = 0;
    for(;x;x-=lowbit(x)) res += c[x];
    return res;
}

void solve(int l, int r, int ql, int qr){
    if(ql > qr) return;
    if(l == r){
        for(int i=ql;i<=qr;i++) ans[id[i]] = l;
        return;
    }
    int mid = (l+r>>1), sum, cnt1 = 0, cnt2 = 0, len = ql-1;
    for(int i=ql;i<=qr;i++){
        if(b[id[i]].opt == 1){
            if(b[id[i]].k <= mid) t1[++cnt1] = id[i], update(b[id[i]].x, b[id[i]].y);
            else t2[++cnt2] = id[i];
        } else {
            sum = query(b[id[i]].y)-query(b[id[i]].x-1);
            if(sum >= b[id[i]].k) t1[++cnt1] = id[i];
            else b[id[i]].k -= sum, t2[++cnt2] = id[i];
        }
    }
    for(int i=ql;i<=qr;i++)
    if(b[id[i]].opt == 1 && b[id[i]].k <= mid) update(b[id[i]].x, -b[id[i]].y);
    for(int i=1;i<=cnt1;i++) id[++len] = t1[i];
    for(int i=1;i<=cnt2;i++) id[++len] = t2[i];
    solve(l, mid, ql, ql+cnt1-1), solve(mid+1, r, ql+cnt1, qr);
}

int main(){
    char opt[2]; int x, y, z; scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++) scanf("%d", &a[i]), b[++cnt] = options(1, i, 1, a[i]);
    for(int i=1;i<=m;i++){
        scanf("%s%d%d", opt, &x, &y);
        if(opt[0] == 'C') b[++cnt] = options(1, x, -1, a[x]), b[++cnt] = options(1, x, 1, y), a[x] = y;
        else scanf("%d", &z), b[++cnt] = options(2, x, y, z);
    }
    for(int i=1;i<=cnt;i++) id[i] = i;
    solve(1, 1e9, 1, cnt);
    for(int i=1;i<=cnt;i++)
        if(b[i].opt == 2) printf("%d\n", ans[i]);
    return 0;
}
posted @ 2018-09-10 20:26  Brave_Cattle  阅读(141)  评论(0编辑  收藏  举报