Dynamic Rankings 带修主席树(主席树 + 主席树维护树状数组)

传送门1-ZOJ
传送门2-洛谷

  学带修主席树的起因是vp吉林省赛的时候队友没看数据范围,然后上网查动态求逆序对,发现是带修主席树,批判我为什么不会,并要求我去学。学完之后发现带修主席树根本过不去,被卡内存了。
  这场的F其实暴力即可

  个人学完这个板子之后觉得板子应该是没有需要动的情况,觉得能做的就是只有动态维护每个位置上的第k小值和维护逆序对数量。

  原理什么的就不说了,自己查一查资料,我觉得对于这种我改不了板子的题没啥必要去讲原理。

  讲讲修改操作怎么用吧,想要在一个位置pos消除某个数值离散花后对应的v对查询的影响,直接MHST.add(pos,v,-1);

  想要在一个位置pos增加某个数值离散花后对应的v对查询的影响,直接MHST.add(pos,v, 1);

  对于查询操作[l, r]需要提前把对应的位置的根存起来,然后进行query查找。

  真不知道这板子能咋改,所以把板子抄来对着题目学一学怎么用就行了,感觉原理不重要。


#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <tuple>
 
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define itn int
#define ll long long
#define ull unsigned long long
#define rep(i, a, b) for(int i = a;i <= b; i ++)
#define per(i, a, b) for(int i = a;i >= b; i --)
#define cntone(x) __builtin_popcount(x)
#define db double
#define fs first
#define se second
#define AC main(void)
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
typedef std::pair<int, int > PII;
typedef std::pair<int, std::pair<int, int>> PIII;
typedef std::pair<ll, ll> Pll;
typedef std::pair<double, double> PDD;
using ld = double long;

const long double eps = 1e-9;
const int N = 6e4 + 10;
int n , m, _;

int a[N];

struct node{
	int l, r, cnt;
}tr[N * 35];

struct Modify_Historical_Segment_Tree{
	int idx;
	//s是树状数组需要用到的根节点
	int root[N], S[N], maxm, stk[N], top;
	//查询时用到的两个数组
	int qr[N], ql[N];
	
	inline void init(){
		idx = top = 0;
	}
	
	inline int lowbit(int x) {return x & -x;}
	
	inline int build(int l, int r){
		int q = ++ idx;
		tr[q].cnt = 0;
		if(l == r)	return q;
		int mid = l + r >> 1;
		tr[q].l = build(l, mid);//存的是左儿子的编号并非边界
		tr[q].r = build(mid + 1, r);
		return q;
	}
	//需要用到的上一个版本root[i - 1](返回的值是当前版本的root) l,r是离散化后的区间范围 需要添加的离散化后的值x sum是添加的数量
	inline int insert(int p, int l, int r, int x, int sum){
		int q = ++ idx;
		tr[q] = tr[p];//复制上一个节点的信息
		if(l == r){
			tr[q].cnt += sum;//新版本的信息加1
			return q;
		}
		int mid = l + r >> 1;
		if(x <= mid)	tr[q].l = insert(tr[p].l, l, mid, x, sum);//在左子树则需要更新信息,否则保留原本信息就可以
		else tr[q].r = insert(tr[p].r, mid + 1, r, x, sum);
		tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
		return q;
	}
	
	//位置为p 改变离散化后值为x的数量 数量
	inline void add(int p, int x, int sum){
		//maxm是树状数组数据离散化后的范围
		while(p <= maxm){
			S[p] = insert(S[p], 1, maxm, x, sum);
			p += lowbit(p);
		}
	}
	//flag 表示是查询哪个数组 1 表示qr数组 0表示ql数组 查询的是左半边修改的数量总和
	inline int Sum(int x, int flag){
		int res = 0;
		while(x){
			if(flag)	res += tr[tr[qr[x]].l].cnt;
			else res += tr[tr[ql[x]].l].cnt;
			x -= lowbit(x);
		}
		return res;
	}

	//我们需要查询1 - lt这个区间在树状数组中左半边修改的总数量所以一般传入l - 1
	//我们需要查询1 - nw这个区间在树状数组中左半边修改的总数量
	// lt nw 是需要去树状数组里面求的版本号所以不用每次query不用改
	inline int query(int lt, int nw, int p, int q, int l, int r, int k){//区间k小
		if(l == r)	return l;
		int mid = l + r >> 1;
		int cnt = Sum(nw, 1) - Sum(lt, 0) + tr[tr[q].l].cnt - tr[tr[p].l].cnt;
		if(cnt >= k){
			//因为区间是逐渐缩小的,所有我们对应 方便Sum去求一个子区间的修改的总数量
			for(int i = nw; i; i -= lowbit(i))	qr[i] = tr[qr[i]].l;
			for(int i = lt; i; i -= lowbit(i))	ql[i] = tr[ql[i]].l;
			return query(lt, nw, tr[p].l, tr[q].l, l, mid, k);
		}	
		else{
			for(int i = nw; i; i -= lowbit(i))	qr[i] = tr[qr[i]].r;
			for(int i = lt; i; i -= lowbit(i))	ql[i] = tr[ql[i]].r;
			return query(lt, nw, tr[p].r, tr[q].r, mid + 1, r, k - cnt);
		} 
	}
}MHST;

struct Query{
	int op, l, r, k;
}q[N];

inline void solve(){
	std::cin >> n >> m;
	std::vector<int> nums;
	MHST.init();
	std::vector<int> pos(n + 1);
	for(int i = 1; i <= n; i ++){
		std::cin >> a[i];
		nums.emplace_back(a[i]);
	}
	for(int i = 1; i <= m; i ++){
		char ch;
		std::cin >> ch;
		if(ch == 'Q'){
			q[i].op = 1;
			std::cin >> q[i].l >> q[i].r >> q[i].k;
		}	
		else{
			q[i].op = 0;
			std::cin >> q[i].l >> q[i].r;
			nums.emplace_back(q[i].r);
		}
	}
	std::sort(nums.begin(), nums.end());
	nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
	int sz = nums.size();
	MHST.maxm = sz;
	for(int i = 1; i <= n; i ++)	pos[i] = std::lower_bound(nums.begin(), nums.end(), a[i]) - nums.begin() + 1;
	auto &root = MHST.root, &S = MHST.S, &qr = MHST.qr, &ql = MHST.ql;
	root[0] = MHST.build(1, sz);
	for(int i = 1; i <= n; i ++)	root[i] = MHST.insert(root[i - 1], 1, sz, pos[i], 1);
	for(int i = 1; i <= sz; i ++)	S[i] = root[0];
	for(int i = 1; i <= m; i ++){
		if(q[i].op){
			int l = q[i].l - 1, r = q[i].r, k = q[i].k;
			for(int j = r; j; j -= MHST.lowbit(j))	qr[j] = S[j];
			for(int j = l; j; j -= MHST.lowbit(j))	ql[j] = S[j];
			std::cout << nums[MHST.query(l, r, root[l], root[r], 1, sz, k) - 1] << '\n';
		}else{
			int x = std::lower_bound(nums.begin(), nums.end(), a[q[i].l]) - nums.begin() + 1;
			MHST.add(q[i].l, x, -1);
			a[q[i].l] = q[i].r;
			x = std::lower_bound(nums.begin(), nums.end(), a[q[i].l]) - nums.begin() + 1;
			MHST.add(q[i].l, x, 1);
		}
	}
}

signed AC{
   	HYS
   	
	std::cin >> _;
	while(_ --)
        solve();

    return 0;
}
posted @ 2023-03-20 09:22  春始于雪之下  阅读(27)  评论(0编辑  收藏  举报