P2617 dynamic ranking dynamic chairman tree (support updating after insertion)

Posted by paldo on Wed, 02 Feb 2022 12:37:15 +0100


Idea:
If you only look at the query operation, it is a very common chairman tree, but a modification operation is added to this topic, which is not available in the previous chairman tree topic. That is, the so-called interval modification, which is to deal with the modification operation first and query instead of sandwiching modification in the query.
So we need another way to solve this problem.

If we were x x Modified at point x a x a_x The value of ax , is obviously in the interval [ x , n ] [x,n] The maintained values in [x,n] should be updated, because the maintenance is equivalent to a prefix information. If we are violent again at this time [ x , n ] [x,n] [x,n] modification complexity is definitely not allowed, so you can think of how to optimize this modification operation.
In fact, the connection with the chairman tree here is to maintain a quantitative relationship of prefix nature. Similarly, if we don't consider that this is a tree, if it is an ordinary one bit array, how do we make a single point modification when maintaining the prefix sum? Tree array / line segment tree.
Then, after considering turning a simple point into a chairman tree, can we use the same idea to realize it? It must be possible. We only need to turn the array maintained by the tree array into the answer array of the chairman tree. namely f o r ( . . . ) for(...) for(...) Every call m o d i f y modify Click modify to update the chairman tree.
In the same way, the query process is similar to the query process of tree array, but every time we want to obtain the value of a point of the chairman tree, we need to follow the query process of the chairman tree, so we use two arrays to maintain the corresponding node of each layer of the current chairman tree r o o t root The root number is enough, and the maintenance is performed in pairs each time.
In this way, the value in a certain interval can be obtained by making a difference between two nodes.
Then it is very smooth to find the second corresponding to each interval k k How big is k?

Then complexity analysis
The obvious time complexity is O ( n ∗ l o g n ∗ l o g n ) O(n*logn*logn) O(n * logn * logn), I think so O ( n ∗ l o g n ∗ l o g n ) O(n * logn * logn) O(n * logn * logn) moves every time it is modified l o g n logn Log n points, and l o w b i t lowbit lowbit is about l o g n logn Log n times, so I think so O ( n ∗ l o g n + m ∗ l o g n ∗ l o g n ) O(n * logn + m * logn * logn) O(n * logn+m * logn * logn), because it involves discretization and then modification, we need to process the query offline. Here n n n represents the range of values after discretization.

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
int n,m,a[N];
struct operation {//Because the data is too large and needs to be discretized, it is necessary to store the inquired information offline
	char op[10];
	int l,r,k,x,y;
}q[N];
int lsh[N * 4],cnt;
int getid(int x) { return lower_bound(lsh + 1,lsh + 1 + cnt,x) - lsh; }
//Time complexity O(n * logn * logn) space should be

/******************Chairman tree with repair (tree array set chairman tree)***********************/
int root[N * 2],tot;
struct ct {
	int l,r,sum;
}tree[N * 25 * 25];
void pushup(int now) { tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum; }
void build(int &now,int l,int r) {
	tree[now = ++tot]; tree[now].l = l,tree[now].r = r;
	if(l == r) { tree[now].sum = 0; return ; }
	int mid = (l + r) >> 1;
	build(tree[now].l,l,mid); build(tree[now].r,mid+1,r);
	pushup(now);
}
void modify(int &now,int pre,int l,int r,int p,int v) {
	tree[now = ++tot] = tree[pre];
	if(l == r) {
		tree[now].sum += v; return ;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) modify(tree[now].l,tree[pre].l,l,mid,p,v);
	else modify(tree[now].r,tree[pre].r,mid+1,r,p,v);
	pushup(now);
}

int lowbit(int x) { return x & (-x); }
int totx,toty,x[N],y[N];
void qwork(int now,int pre) {//This is the query interval
	totx = toty = 0;
	for(int i = pre - 1;i;i -= lowbit(i)) x[++totx] = root[i];
	for(int i = now;i;i -= lowbit(i)) y[++toty] = root[i];
}

int query(int l,int r,int k) {//The query process includes the lowbit idea of tree array
	if(l == r) return l;
	int mid = (l + r) >> 1;
	int ss = 0;//Prefix and interval summation, the first few are all subtracted, which is actually simulating the summation process of tree array
	for(int i = 1;i <= totx;i ++) ss -= tree[tree[x[i]].l].sum;
	for(int i = 1;i <= toty;i ++) ss += tree[tree[y[i]].l].sum;
	if(k <= ss) {
		for(int i = 1;i <= totx;i ++) x[i] = tree[x[i]].l;
		for(int i = 1;i <= toty;i ++) y[i] = tree[y[i]].l;
		return query(l,mid,k);
	}
	else {
		for(int i = 1;i <= totx;i ++) x[i] = tree[x[i]].r;
		for(int i = 1;i <= toty;i ++) y[i] = tree[y[i]].r;
		return query(mid+1,r,k-ss);
	}
}
void add(int pos,int modifyx,int v) {//Is to change the ordinary update into the logn update of the tree
	for(int i = pos;i <= cnt;i += lowbit(i)) {
		modify(root[i],root[i],1,cnt,modifyx,v);
	}
}
/***********************************************************/

int main() {
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i ++) {
		scanf("%d",&a[i]);
		lsh[++cnt] = a[i];
	}
	for(int i = 1;i <= m;i ++) {
		scanf("%s",q[i].op);
		if(q[i].op[0] == 'Q') {
			scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
		}
		else {
			scanf("%d%d",&q[i].x,&q[i].y);
			lsh[++cnt] = q[i].y;
		}
	}
	sort(lsh + 1,lsh + 1 + cnt);
	cnt = unique(lsh + 1,lsh + 1 + cnt) - lsh - 1;
	build(root[0],1,cnt);
	// cout << "--------\n";
	for(int i = 1;i <= n;i ++) {
		add(i,getid(a[i]),1);//In fact, it can also be divided into two arrays, one to maintain the initial static chairman tree and the other to maintain the subsequent dynamic chairman tree with the idea of bit
	}
	// cout << "-------\n";
	for(int i = 1;i <= m;i ++) {
		if(q[i].op[0] == 'C') {
			int p = q[i].x,v = q[i].y;
			// cout << q[i].x << ' ' << q[i].y << ' ' << getid(a[q[i].x]) << ' ' << getid(v) << '\n';
			add(p,getid(a[p]),-1);
			add(p,getid(v),1);
			// cout << "----\n";
			a[p] = v;
		}
		else {
			qwork(q[i].r,q[i].l);
			// cout << "-----\n";
			int ans = query(1,cnt,q[i].k);
			printf("%d\n",lsh[ans]);
		}
	}
	return 0;
}

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

Topics: data structure