2019icpc徐州站 H题 Yuuki and a problem(树套树(树状数组套主席树))

题目链接

题目大意:

第一行给出两个整数n,m,n代表的是数组的大小,m代表询问的个数,对于每个询问有两种形式,
1 x y 表示的是将数组a[x]改为y,2 x y 表示的是查询从x到y的序列中不能表示出的最小的数是多少,这个题目的推导在我前几天写的博客里面有提到,有兴趣的可以去看一下【题目链接】现在问题的落脚点就是怎么求一段区间的和了,因为这个数组中有n个值,那么就代表着权值线段树有n+1个版本,如果第i个版本更新了,那么很显然(i,n)的所有版本都要给他更新,这时候复杂度可就大了,咱们可以考虑用树状数组优化这个过程,就是优化每次更新的下标,其实跟传统的一样,只是树状数组的值是一棵棵权值线段树,具体请看代码吧:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 200010;
typedef long long LL;
int read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}
void write(int x) {
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int n,q;
struct node {
	int l,r;
	LL sum;
};
int A[30],B[30],cnt1,cnt2;
int idx,root[N],a[N];
node tr[N<<7];
inline int lowbit(int x) {
	return x & -x;
}
void insert(int &k,int l,int r,int pos,int val) {
	if(!k) k = ++idx;
	tr[k].sum += val;
	if(l == r) return;
	int mid = l + r >> 1;
	if(pos <= mid) insert(tr[k].l,l,mid,pos,val);
	else insert(tr[k].r,mid+1,r,pos,val);
}
LL query(int l,int r,int L,int R) {
	if(l >= L && r <= R) {
		LL ans = 0;
		for(int i=1; i<=cnt1; i++) ans -= tr[A[i]].sum;
		for(int i=1; i<=cnt2; i++) ans += tr[B[i]].sum;
		return ans;
	}
	int mid = l + r >> 1;
	int A1[30],B1[30];

	LL ans = 0;
	if(mid >= L) {
		for(int i=1; i<=cnt1; i++) A1[i] = A[i],A[i] = tr[A[i]].l;
		for(int j=1; j<=cnt2; j++) B1[j] = B[j],B[j] = tr[B[j]].l;
		ans += query(l,mid,L,R);
		for(int i=1; i<=cnt1; i++) A[i] = A1[i];
		for(int j=1; j<=cnt2; j++) B[j] = B1[j];
	}
	if(R > mid) {
		for(int i=1; i<=cnt1; i++) A1[i] = A[i],A[i] = tr[A[i]].r;
		for(int j=1; j<=cnt2; j++) B1[j] = B[j],B[j] = tr[B[j]].r;
		ans += query(mid+1,r,L,R);
		for(int i=1; i<=cnt1; i++) A[i] = A1[i];
		for(int j=1; j<=cnt2; j++) B[j] = B1[j];
	}
	return ans;
}
int main() {
	n = read();
	q = read();
	for(int i=1; i<=n; i++) {
		a[i] = read();
		for(int j=i; j<=n; j+=lowbit(j)) {
			insert(root[j],1,N,a[i],a[i]);
		}
	}
	while(q--) {
		int op = read();
		if(op == 1) {
			int x = read(),y = read();
			for(int j=x; j<=n; j+=lowbit(j)) {
				insert(root[j],1,N,a[x],-a[x]);
			}
			a[x] = y;
			for(int j=x; j<=n; j+=lowbit(j)) {
				insert(root[j],1,N,a[x],a[x]);
			}
		} else {
			int l = read(),r = read();
			cnt1 = cnt2 = 0;
			for(int i=l-1; i; i-=lowbit(i)) A[++cnt1] = root[i];
			for(int j=r; j; j-=lowbit(j)) B[++cnt2] = root[j];
			LL st = 0,pre = 0;
			while(1){
				st = query(1,N,1,min(st+1,1ll*N));
				if(st == pre) break;
				pre = st;
			}
			printf("%lld\n", st + 1);
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波一打七~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值