【模拟赛|ZROI】史莱姆(异或,数据结构)

题面

【1s , 512MB】
在这里插入图片描述
样例输入

7 5
1 85
1 69
1 24
1 82
2 71

样例输出

3

在这里插入图片描述

题解

首先有个基础结论:对于任意 a ≤ b ≤ c a\leq b\leq c abc min ⁡ ( a ⊕ b , b ⊕ c ) ≤ a ⊕ c \min(a\oplus b,b\oplus c)\leq a\oplus c min(ab,bc)ac

证明很简单,考虑到 01 串的 tri 树是个二叉树,三个叶子放上去,相邻两个的 l c a lca lca 总有一个比三个的 l c a lca lca 深度大。

所以,我们只需要关注集合中大小相邻的 i , j i,j i,j 就好。

还有另一个很重要的结论: a ⊕ b ≥ a − b a\oplus b\geq a-b abab ,把异或当作不退位的减法就行。

有这样的结论后,我们想想怎么用。我们的算法可以基于这样一个暴力:对于每一对相邻的 i , j i,j i,j ,从小到大枚举 x x x ,将所有 ( i + x ) ⊕ ( j + x ) (i+x)\oplus(j+x) (i+x)(j+x) 成为前缀最小值 x x x 记录下来。加入我们枚举到 x + i = a , x + j = b x+i=a,x+j=b x+i=a,x+j=b ,不妨设 l o w b i t ( a ) ≥ l o w b i t ( b ) lowbit(a)\geq lowbit(b) lowbit(a)lowbit(b) ,我们接下来枚举的数(假设是 x + y x+y x+y)如果甚至没导致 b b b l o w b i t ( a ) lowbit(a) lowbit(a) 位进位的话,那么一定不会是前缀最小值,因为这时我们可以单独考虑两个数的最后 l o w b i t ( a ) lowbit(a) lowbit(a) 位,单独考虑这些位时 a a a y y y b b b y + k y+k y+k ,而在加 y y y 之前 a a a 0 0 0 b b b k k k ,两个异或值等于两数之差,根据上一段的结论, x + y x+y x+y 一定不比 x x x 更优。所以,我们每次向前枚举的时候,要增加一个数 y y y 使得 b b b l o w b i t ( a ) lowbit(a) lowbit(a)刚好变化。这样一来,每一位最多贡献两个前缀最小值,总的枚举复杂度为 O ( V ) O(V) O(V)

于是我们总共只有 O ( n V ) O(nV) O(nV) 个三元组 ( x , x + i , x + j ) (x,x+i,x+j) (x,x+i,x+j) 会产生贡献,维护这些三元组就好了。

我使用了平衡树统计答案(因为空间很紧),所以还得卡常。

还有个结论可以大大剪枝:令当前的 ∣ i − j ∣ |i-j| ij 最小值为 l m lm lm ,若询问的 w ≥ l m w\geq lm wlm ,我们一定可以在 [ 0 , w ] [0,w] [0,w] 中找到解,使得答案为 l m lm lm ,这刚好是答案的理论最小值。所以只需要考虑 w < l m w<lm w<lm,对于 x ≥ l m x\geq lm xlm 的三元组 ( x , x + i , x + j ) (x,x+i,x+j) (x,x+i,x+j) 也可以直接去掉了。

时间复杂度 O ( n V log ⁡ n ) O(nV\log n) O(nVlogn)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
struct it{
	int s[2],hp,siz;
	LL ky,nm,mi;
	it(){s[0]=s[1]=hp=siz=ky=0;nm=mi=1e18;}
}tre[MAXN*80];
set<LL> st;
int cnt = 0;
stack<int> rab;
mt19937 ji(*new int);
int newp() {
	if(!rab.empty()) {
		int x = rab.top();rab.pop();
		tre[x] = it(); tre[x].hp = ji();
		return x;
	}tre[++ cnt].hp = ji();
	return cnt;
}
int upd(int x) {
	tre[x].mi = min(tre[tre[x].s[0]].mi,min(tre[tre[x].s[1]].mi,tre[x].nm));
	return x;
}
void spli(int x,LL k,LL nb,int &a,int &b) {
	if(!x) {a=b=0;return ;}
	if((pair<LL,LL>){tre[x].ky,tre[x].nm} <= (pair<LL,LL>){k,nb}) {
		spli(tre[x].s[1],k,nb,tre[x].s[1],b);
		a = upd(x); return ;
	}
	spli(tre[x].s[0],k,nb,a,tre[x].s[0]);
	b = upd(x); return ;
}
int merg(int a,int b) {
	if(!a || !b) return a|b;
	if(tre[a].hp < tre[b].hp) {
		tre[a].s[1] = merg(tre[a].s[1],b);
		return upd(a);
	}
	tre[b].s[0] = merg(a,tre[b].s[0]);
	return upd(b);
}
void ins(int &x,LL A,LL B) {
	int a,b,c; spli(x,A,B-1,a,b); spli(b,A,B,b,c);
	if(b) tre[b].siz ++;
	else {b = newp();tre[b].siz=1; tre[b].ky = A; tre[b].nm=tre[b].mi=B;}
	x = merg(a,merg(b,c)); return ;
}
void del(int &x,LL A,LL B) {
	int a,b,c; spli(x,A,B-1,a,b); spli(b,A,B,b,c);
	if(b) {tre[b].siz--; if(tre[b].siz==0) rab.push(b),b=0;}
	x = merg(a,merg(b,c)); return ;
}
LL query(int &x,LL M) {
	int a,b; spli(x,M+1,-1,a,b);
	LL as = tre[a].mi; x = merg(a,b);
	return as;
}
void rec(int x) {
	if(!x) return ;
	rec(tre[x].s[0]);
	rab.push(x);
	rec(tre[x].s[1]);
	return ;
}
int rt = 0;
LL MX,gap;
void ADD(LL a,LL b) {
	LL x = 0;
	ins(rt,x,a^b);
	while(x < gap) {
		if(lowbit(a) > lowbit(b)) swap(a,b);
		LL k = lowbit(b),ad = k - (a & (k-1));
		x += ad; a += ad; b += ad;
		if(a > b) swap(a,b);
		ins(rt,x,a^b);
	}return ;
}
void DEL(LL a,LL b) {
	LL x = 0;
	del(rt,x,a^b);
	while(x < gap) {
		if(lowbit(a) > lowbit(b)) swap(a,b);
		LL k = lowbit(b),ad = k - (a & (k-1));
		x += ad; a += ad; b += ad;
		if(a > b) swap(a,b);
		del(rt,x,a^b);
	}return ;
}
int main() {
	m = read();n = read();
	MX = 1ll<<m; gap = MX;
	for(int i = 1;i <= n;i ++) {
		int op = read();
		LL w = read();
		if(op == 1) {
			st.insert(w);
			auto j = st.find(w);
			auto l = j,r = j; r ++;
			if(l != st.begin() && r != st.end()) {
				l --; DEL(*l,*r); l ++;
			}
			if(l != st.begin()) {
				l --;
				gap = min(gap,w-*l);
				ADD(*l,w);
			}
			if(r != st.end()) {
				gap = min(gap,*r-w);
				ADD(w,*r);
			}
		}
		else {
			LL as = query(rt,w);
			if(w >= gap) as = gap;
			AIput(as,'\n');
		}
		int b; spli(rt,gap+1,-1,rt,b);
		rec(b);
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值