牛客第五场 H Interval —— 主席树

题目链接:点我啊╭(╯^╰)╮

题目大意:

     F ( l , r ) = A l & A l + 1 & . . . & A r F(l, r) = A_l \& A_{l+1} \& ... \& A_{r} F(l,r)=Al&Al+1&...&Ar
     S ( l , r ) = { F ( a , b ) ∣ m i n ( l , r ) ≤ a ≤ b ≤ m a x ( l , r ) } S(l, r) = \{F(a, b) | min(l, r) ≤ a ≤ b ≤ max(l, r)\} S(l,r)={F(a,b)min(l,r)abmax(l,r)}
     Q Q Q 次查询 l l l r r r,求 S ( l , r ) S(l, r) S(l,r)

解题思路:

    一个数字连续作与操作,不同数字最多出现 l o g log log
    因此对每个 a i a_i ai,记录它往左不停作与操作出现的不同数字
    这里我们要查询 S ( l , r ) S(l, r) S(l,r),也就是以 r r r 为右端点,做与操作的不同数字且产生过程都在 l l l 右端
    联想到主席树求区间不同数字的操作
    这里就可以在记录 a i a_i ai 往左不停作与操作出现的不同数字 的同时
    维护产生这些数字的左端点的最大值
    这个最大值 m a x max max 就是我们查询 S ( l , r ) S(l, r) S(l,r) 时, l l l 必须要 ≤ m a x ≤max max
    因为所有的不同数字最多 n l o g n nlogn nlogn 个,因此直接用 m a p map map 维护最大值即可
    时间复杂度: O ( n l o g 2 n + q l o g n ) O(nlog^2n +qlogn) O(nlog2n+qlogn)

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 1e5 + 5;
int T, n, q, lans, tot, root[maxn];
int t[maxn*300], ls[maxn*300], rs[maxn*300];
map <int, int> mp, tmp, las;

void update(int &rt, int pre, int pos, int c, int l, int r) {
	if(l>pos || r<pos) return;
	rt = ++tot;
	t[rt] = t[pre] + c;
	ls[rt] = ls[pre], rs[rt] = rs[pre];
	if(l == r) return;
	int mid = l + r >> 1;
	update(ls[rt], ls[pre], pos, c, l, mid);
	update(rs[rt], rs[pre], pos, c, mid+1, r);
}

int query(int rt, int L, int R, int l, int r) {
	if(l>R || r<L) return 0;
	if(l>=L && r<=R) return t[rt];
	int mid = l + r >> 1, ret = 0;
	ret += query(ls[rt], L, R, l, mid);
	ret += query(rs[rt], L, R, mid+1, r);
	return ret;
}

signed main() {
	scanf("%d", &n);
	for(int i=1, x; i<=n; ++i) {
		scanf("%d", &x);
		root[i] = root[i-1];
		mp[x] = i; tmp.clear();
		for(auto j : mp) {
			int fi = j.first, se = j.second;
			tmp[fi & x] = max(tmp[fi & x], se);
		}
		for(auto j : tmp) {
			int fi = j.first, se = j.second;
			if(las[fi] == se) continue;
			update(root[i], root[i], las[fi], -1, 1, n);
			las[fi] = se;
			update(root[i], root[i], las[fi], 1, 1, n);
		}
		mp.swap(tmp);
	}
	scanf("%d", &q);
	while(q--) {
		int l, r;
		scanf("%d%d", &l, &r);
		l = (l ^ lans) % n + 1;
		r = (r ^ lans) % n + 1;
		if(l > r) swap(l, r);
		printf("%d\n", lans = query(root[r], l, r, 1, n));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值