2019ICPC徐州 H - Yuuki and a problem (动态(带修)主席树 + 区间Mex问题)

题意:

给你一个长度为 n n n的序列,然后 m m m个询问,你有两种操作,定义如下:
1.   1   x   y 1.\ 1\ x\ y 1. 1 x y,即令 a x = y a_x = y ax=y,修改操作.
2.   2   l   r 2.\ 2 \ l \ r 2. 2 l r,表示询问你区间 [ l , r ] [l,r] [l,r]内所有的数的 M e x Mex Mex是多少,即这 r − l + 1 r - l + 1 rl+1个数所不能组成的最小的正整数是多少?

思路:

哇昆明出题人不讲武德,也是自己的锅,当时很久之前是训练过2019徐州的,但是还是没有看到这个题,昆明 M M M就是这个题的简化版,少了一个 l o g log log,无动态的要求。
那就思路操作就很明显了,就和昆明的 M M M题一样的操作,具体操作思路和复杂度证明 移步另一篇博客,然后这个题就是在那个题的基础上加上了动态主席树罢了,剩下的操作完全相同。

当做套路题好好掌握起来啊。
emmmm不开LL 会超时滴,写结构体也会超时滴,不知道为何,是常数太大嘛,但也不应该qwq,以后还是写数组的吧

#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 = 2e5 + 10;
int n,m,cnt,a[N];
ll tree[N * 150];
int ls[N * 150],rs[N * 150],root[N],tot;
int totx,toty,x[N],y[N];
//这尼玛就离谱 昆明整个2019徐州原题 还不用带修 艹 我为什么不早补
void modify(int &now,int pre,int l,int r,int p,int v) {
	if(!now) now = ++tot;
	tree[now] = tree[pre],ls[now] = ls[pre],rs[now] = rs[pre];
	if(l == r) {
		tree[now] += v; return ;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) modify(ls[now],ls[pre],l,mid,p,v);
	else modify(rs[now],rs[pre],mid+1,r,p,v);
	tree[now] = tree[ls[now]] + tree[rs[now]];
}
ll query(int l,int r,int R) {
	if(r <= R) {
		ll ss = 0;
		for(int i = 1;i <= totx;i ++) ss -= tree[x[i]];
		for(int i = 1;i <= toty;i ++) ss += tree[y[i]];
		return ss;
	}
	int mid = (l + r) >> 1;
	if(R <= mid) {
		for(int i = 1;i <= totx;i ++) x[i] = ls[x[i]];
		for(int i = 1;i <= toty;i ++) y[i] = ls[y[i]];
		return query(l,mid,R);
	}
	else {//不开多个数组记录一下的话 要保证走完这一层不会再返回来了
		ll ss = 0;
		for(int i = 1;i <= totx;i ++) ss -= tree[ls[x[i]]];
		for(int i = 1;i <= toty;i ++) ss += tree[ls[y[i]]];

		for(int i = 1;i <= totx;i ++) x[i] = rs[x[i]];
		for(int i = 1;i <= toty;i ++) y[i] = rs[y[i]];
		return ss + query(mid+1,r,R);
	}
}
inline int lowbit(int x) { return x & (-x); }
void add(int pos,int vpos,int v) {//pos 对应区间即bit修改的位置 vpos对应值域即主席树修改的位置
	for(int i = pos;i <= n;i += lowbit(i)) {
		modify(root[i],root[i],1,200000,vpos,v);
	}
}
void work(int l,int r) {
	totx = 0,toty = 0;
	for(int i = l - 1;i;i -= lowbit(i)) x[++totx] = root[i];
	for(int i = r;i;i -= lowbit(i)) y[++toty] = root[i]; 
}

int main() {
	scanf("%d%d",&n,&m);
	// build(root[0],1,200000);
	for(int i = 1;i <= n;i ++) {
		scanf("%d",&a[i]);
		add(i,a[i],a[i]);
	}
	int op,l,r,x,y;
	for(int i = 1;i <= m;i ++) {
		scanf("%d",&op);
		if(op == 1) {
			scanf("%d%d",&x,&y);
			add(x,a[x],-a[x]);
			add(x,y,y);
			a[x] = y;
		}
		else {
			scanf("%d%d",&l,&r);
			ll now = 1,sum = 0;
			while(true) {
				work(l,r);
				int MAX = min(1LL * 200000,now);
				// cout << "f + 1 = "<< MAX << '\n';
				ll tmp = query(1,200000,MAX);
				// cout << "ans = " << ff << '\n';
				if(tmp == sum) {
					printf("%lld\n",now); break;
				}
				// f = ff;
				sum = tmp; now = sum + 1;
			}
			// printf("%lld\n",f + 1);
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值