洛谷2617 Dynamic Rankings(带修主席树学习笔记)

qwq
菜的真实
过完年一点都不想学习。

首先我们看这个题,一个带修主席树的板子题。
qwq
首先,如果选择暴力修改,每次会修改 O ( n ) O(n) O(n)级别的主席树,这个复杂度是没有办法接受的。

但是我们学过一个东西叫树状数组 B I T BIT BIT
他的思想,是将修改和查询的复杂度都变成了 l o g log log
相当于是带修前缀和。
既然主席树也是前缀和的形式。

那么这时候呢,我们不妨稍微改变一下主席树的构造方式。

也就是说,牺牲一些查询前缀和的速度,但是加快我们修改的速度。

考虑对于一颗主席树 r o o t [ i ] root[i] root[i]维护的是 [ i − l o w b i t ( i ) + 1 , i ] [i-lowbit(i)+1,i] [ilowbit(i)+1,i]的和,那么对于构造主席树的时候呢,对于一个位置来说,需要修改log棵的主席树。

可以理解成一个树状数组结构的主席树

void add(int pos,int x,int p)
{
	int val = getnum(x);
	for (int i=pos;i<=n;i+=lowbit(i))
	  root[i]=modify(root[i],1,sz,val,p);
}

那这样修改的问题就迎刃而解了
首先把原来权值的数量-1,然后新的权值的数量+1

那么应该怎么查询呢?
qwq由于我们把修改的速度变快了,所以查询的速度也要相应的变慢一些。

考虑将 [ l , r ] [l,r] [l,r]转化成 [ 1 , r ] − [ 1 , l − 1 ] [1,r] - [1,l-1] [1,r][1,l1]

那么我们首先把 l − 1 和 r l-1和r l1r进行拆分,拆分成log个 r o o t root root的形式,因为我们维护的主席树就是一段一段的

for (int j=q[i].x-1;j;j-=lowbit(j)) l[++num1]=root[j]; //分别把左右区间进行拆解 
for (int j=q[i].y;j;j-=lowbit(j)) r[++num2]=root[j]; 

q u e r y query query的时候呢,对于左端点拆出来的端点的 r o o t root root v a l val val是减,右端点是加。

如果要进入左子树,那么所有拆出来的部分 都要进入左子树
qwq
感觉比较难说明白
还是看代码会比较好

int query(int lll,int rr,int k)
{
	if (lll==rr) return lll;
	int mid = lll+rr >> 1;
	int vall=0;
	for (int i=1;i<=num1;i++) vall-=t[t[l[i]].l].val;
	for (int i=1;i<=num2;i++) vall+=t[t[r[i]].l].val; //相当于把区间[l,r]转化成[1,r]-[1,l-1] 
	if (vall>=k)
	{
		for (int i=1;i<=num1;i++) l[i]=t[l[i]].l;
		for (int i=1;i<=num2;i++) r[i]=t[r[i]].l;
		return query(lll,mid,k); 
	}
	else
	{
		for (int i=1;i<=num1;i++) l[i]=t[l[i]].r;
		for (int i=1;i<=num2;i++) r[i]=t[r[i]].r;
		return query(mid+1,rr,k-vall);
	} 
}

那么到这里差不多就结束了
qwq
感觉思路还是很巧妙的
qwq
下面放一下整个题的代码

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 2e5+1e2;

struct Node{
	int l,r,val;
};

Node t[maxn*20*10];
int n,m;

struct pp{
  int opt;
  int x,y,k;
};

pp q[maxn];

int val[maxn],root[maxn];
int a[maxn];
int sz;
vector<int> v;
int l[maxn],r[maxn];
int num1,num2;
int tot;

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

void up(int root)
{
	t[root].val=t[t[root].l].val+t[t[root].r].val;
}

int getnum(int x)
{
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}


int modify(int pre,int l,int r,int x,int p)
{
	int now = ++tot;
	if (l==r)
	{
		t[now].val=t[pre].val+p;
		return now;
	}
	int mid = l+r >> 1;
	
	if (x<=mid) 
	{
		t[now].r=t[pre].r;
		t[now].l=modify(t[pre].l,l,mid,x,p);
	}
	else
	{
		t[now].l=t[pre].l;
		t[now].r=modify(t[pre].r,mid+1,r,x,p);
	}
	up(now);
	return now;
}

void add(int pos,int x,int p)
{
	int val = getnum(x);
	for (int i=pos;i<=n;i+=lowbit(i))
	  root[i]=modify(root[i],1,sz,val,p);
}

int query(int lll,int rr,int k)
{
	if (lll==rr) return lll;
	int mid = lll+rr >> 1;
	int vall=0;
	for (int i=1;i<=num1;i++) vall-=t[t[l[i]].l].val;
	for (int i=1;i<=num2;i++) vall+=t[t[r[i]].l].val; //相当于把区间[l,r]转化成[1,r]-[1,l-1] 
	if (vall>=k)
	{
		for (int i=1;i<=num1;i++) l[i]=t[l[i]].l;
		for (int i=1;i<=num2;i++) r[i]=t[r[i]].l;
		return query(lll,mid,k); 
	}
	else
	{
		for (int i=1;i<=num1;i++) l[i]=t[l[i]].r;
		for (int i=1;i<=num2;i++) r[i]=t[r[i]].r;
		return query(mid+1,rr,k-vall);
	} 
}

int main()
{
  n=read();m=read();
  for (int i=1;i<=n;i++) val[i]=read(),v.pb(val[i]);
  for (int i=1;i<=m;i++)
  {
  	 char s[10];
  	 scanf("%s",s+1);
  	 if (s[1]=='Q') 
  	 {
  	 	q[i].opt=1;
  	 	q[i].x=read();
  	 	q[i].y=read();
  	 	q[i].k=read();
	 }
	 else
	 {
	 	q[i].opt=2;
	 	q[i].x=read();
	 	q[i].y=read();
	 	v.pb(q[i].y);
	 }
  }
  sort(v.begin(),v.end());
  sz = unique(v.begin(),v.end())-v.begin();
  v.resize(sz);
  for (int i=1;i<=n;i++) add(i,val[i],1);
  
  //cout<<1<<endl;
  for (int i=1;i<=m;i++)
  {
     if (q[i].opt==1)
     {
     	
     	num1=num2=0;
     	for (int j=q[i].x-1;j;j-=lowbit(j)) l[++num1]=root[j]; //分别把左右区间进行拆解 
     	for (int j=q[i].y;j;j-=lowbit(j)) r[++num2]=root[j]; 
     //	cout<<num1<<" "<<num2<<endl;
     	int now = query(1,sz,q[i].k)-1;
     	//cout<<now<<endl;
		cout<<v[now]<<"\n";
	 }
	 else
	 {
	 	add(q[i].x,val[q[i].x],-1);
	 	val[q[i].x]=q[i].y;
	 	add(q[i].x,val[q[i].x],1);
	 }
	 //cout<<"***"<<endl;
  }
  return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值