ZOJ 2112 Dynamic Rankings 树状数组套主席树 单点修改求动态区间第K大

题目大意:

就是给出一个数列之后进行两种操作:

1. 询问从第l个数到第r个数中的第k小的数

2. 修改某个位置的数为t


大致思路:

主席树学习第二题...

就是对原序列建立主席树然后用树状数组来维护另外一颗主席树来表示原主席树的变化

因为每次修改都会导致需要修改一整段线段树, 那么考虑用树状数组维护, 每个位置都是一颗线段树, 这些线段树的前缀和就是对应位置的变化, 在递归查询的时候考虑这些和和原主席树的和即可


代码如下:

Result  :  Accepted     Memory  :  29648 KB     Time  :  170 ms

/*
 * Author: Gatevin
 * Created Time:  2015/9/9 21:45:45
 * File Name: BIT_ChairTree.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 树状数组套主席树动态求区间第K大
 * 支持操作为单点修改
 * 首先对于原来的序列建立主席树
 * 考虑用树状数组维护一堆线段树代表发生在主席树上的变化, 当修改一个点的值的时候, 利用树状数组对一段线段树
 * 在每次询问的时候就是在原来的主席树的基础上机上由树状数组维护的这写变化情况即可
 * 由于只建立了两颗主席树, 空间复杂度在O(nlongn)级别
 * 初始建立主席树的时间复杂度是O(nlogn), 对于每次查询, 回答的时间复杂度是O((logn)^2)
 * 整体的时间复杂度是O(nlogn + m*(logn)^2)
 */

#define maxn 60010
#define maxm 10010

namespace ChairTree
{
    struct Node
    {
        int ls, rs, w;
        Node(){ ls = rs = w = 0; }
    };
    Node T[maxn * 20 * 2];
    int a[maxn];//需要处理的数组
    int rooto[maxn];//原数组建立的主席树
    int root[maxn];//树状数组维护的主席树
    int sz;//当前总结点数(包括树状数组维护的和原本的)
    int N;//所有数值离散之后的范围是[1, N];
    
    struct Op//操作
    {
        int type, l, r, k;
        Op(){ type = l = r = k = 0; }
    };
    Op op[maxm];
    
    int n, m;
    vector<int> Ql, Qr;
    vector<int> disc;//离散化用的
    
    int lowbit(int x)
    {
        return -x & x;
    }
    
    void build(int &i, int l, int r, int x)
    {
        T[++sz] = T[i];
        i = sz;
        T[i].w++;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(x <= mid) build(T[i].ls, l, mid, x);
        else build(T[i].rs, mid + 1, r, x);
    }
    
    void insert(int &i, int l, int r, int x, int flag)
    {
        if(i == 0)
        {
            T[++sz] = T[i];
            i = sz; 
        }
        T[i].w += flag;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(x <= mid) insert(T[i].ls, l, mid, x, flag);
        else insert(T[i].rs, mid + 1, r, x, flag);
    }
    
    void BIT_insert(int pos, int x, int flag)//将pos位置修改, flag == -1表示擦去x, 否则表示添上x
    {
        for(int i = pos; i <= N; i += lowbit(i))
            insert(root[i], 1, N, x, flag);
        return;
    }
    
    int ST_query(vector<int> Q1, vector<int> Q2, int l, int r, int k)
    {
        if(l == r) return l;
        int c = 0;
        int mid = (l + r) >> 1;
        for(int i = 0, sz = Q1.size(); i < sz; i++) c -= T[T[Q1[i]].ls].w;
        for(int i = 0, sz = Q2.size(); i < sz; i++) c += T[T[Q2[i]].ls].w;
        for(int i = 0, sz = Q1.size(); i < sz; i++) Q1[i] = (c >= k ? T[Q1[i]].ls : T[Q1[i]].rs);
        for(int i = 0, sz = Q2.size(); i < sz; i++) Q2[i] = (c >= k ? T[Q2[i]].ls : T[Q2[i]].rs);
        if(c >= k) return ST_query(Q1, Q2, l, mid, k);
        else return ST_query(Q1, Q2, mid + 1, r, k - c);
    }
    
    int query(int l, int r, int k)
    {
        Ql.clear(), Qr.clear();
        Ql.push_back(rooto[l - 1]);
        Qr.push_back(rooto[r]);
        for(int i = l - 1; i > 0; i -= lowbit(i)) Ql.push_back(root[i]);
        for(int i = r; i > 0; i -= lowbit(i)) Qr.push_back(root[i]);
        return ST_query(Ql, Qr, 1, N, k);
    }
    
    int find(int x)
    {
        return lower_bound(disc.begin(), disc.end(), x) - disc.begin() + 1;
    }
    
    
    void solve()
    {
        int cas;
        scanf("%d", &cas);
        while(cas--)
        {
            sz = 0;
            memset(rooto, 0, sizeof(rooto));
            memset(root, 0, sizeof(root));//?
            disc.clear();
            scanf("%d %d", &n, &m);
            for(int i = 1; i <= n; i++)
            {
                scanf("%d", a + i);
                disc.push_back(a[i]);
            }
            char s[4];
            for(int i = 1; i <= m; i++)
            {
                scanf("%s", s);
                if(s[0] == 'Q')
                {
                    op[i].type = 1;
                    scanf("%d %d %d", &op[i].l, &op[i].r, &op[i].k);
                }
                else
                {
                    op[i].type = 0;
                    scanf("%d %d", &op[i].l, &op[i].r);
                    disc.push_back(op[i].r);
                }
            }
            sort(disc.begin(), disc.end());
            disc.resize(N = unique(disc.begin(), disc.end()) - disc.begin());
            
            for(int i = 1; i <= n; i++)
            {
                rooto[i] = rooto[i - 1];
                build(rooto[i], 1, N, find(a[i]));
            }
            
            for(int i = 1; i <= m; i++)
            {
                if(op[i].type == 1)
                {
                    int ans = query(op[i].l, op[i].r, op[i].k);
                    printf("%d\n", disc[ans - 1]);
                }
                else//将a[op[i].l]改为op[i].r
                {
                    BIT_insert(op[i].l, find(a[op[i].l]), -1);
                    BIT_insert(op[i].l, find(op[i].r), 1);
                    a[op[i].l] = op[i].r;
                }
            }
        }
    }
};

int main()
{
    ChairTree::solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值