2020ICPC济南K Kth Query

题目链接

题目:

中文描述不清,不如直接看题面
在这里插入图片描述
( 1 ≤ n , q ≤ 1 0 5 , 0 ≤ a i < 2 30 , 1 ≤ L < R < 2 30 , 1 ≤ K ≤ n ) (1 \le n,q \le 10^5,0 \le a_i <2^{30},1 \le L<R<2^{30},1 \le K \le n) (1n,q105,0ai<230,1L<R<230,1Kn)

题解:

如果对01trie的基本操作不是很熟悉,可以参考这里
这种异或的操作显然可以在01trie上实现。先将序列 a a a中所有的元素插入到01trie中。
首先考虑单个 f ( a , S , K ) f(a,S,K) f(a,S,K)怎么实现。其中第一个问题是如何实现将序列 a a a中所有的元素异或上 S S S?如果 S S S的二进制表示从高到底第 i i i位为1,那么就将01trie中第 i i i层结点的左右子树互换,如果第 i i i位为0,就不操作。第二个问题是如何找第 K K K小?因为01trie本质上就是一棵权值线段树,所以可以照搬权值线段树找第 K K K小的树上二分的方法来实现,即每个结点 u u u维护一个信息 s z u sz_u szu,表示以 u u u为根节点的子树中有多少个叶子(即结点 u u u管着多少个 a i a_i ai),如果左子结点的 s z sz sz大于等于 K K K,那么第 K K K小就在左子树的叶子结点中,往左走,否则往右走。
然后考虑一个这个问题的简化版,如果没有 L L L R R R的限制,即求 min ⁡ 0 ≤ S < 2 30 f ( a , S , K ) \min \limits_{0 \le S <2^{30}}{f(a,S,K)} 0S<230minf(a,S,K)。可以发现这个问题是不能从高到低位贪心的,因为有后效性,但可以用 d p dp dp来解。令 k t h u , i kth_{u,i} kthu,i表示从结点 u u u往下可以得到的最小的 f ( a , S , i ) f(a,S,i) f(a,S,i),然后转移方程就是:
(假设结点 u u u处于第 d d d层,即现在要决策的是 S S S的第 d d d位)
(1)假设 S S S的该位为0,那么可得
a n s 1 = { k t h l s o n u , i l s o n u ∃   & & s z l s o n u ≤ i 2 30 − d + k t h r s o n u , i − s z l s o n u e l s e ans1=\begin{cases} kth_{lson_u,i} \quad\quad\quad lson_u \exist \ \&\&sz_{lson_u} \le i \\ 2^{30-d}+kth_{rson_u,i-sz_{lson_u}} \quad else \end{cases} ans1={kthlsonu,ilsonu &&szlsonui230d+kthrsonu,iszlsonuelse
(2)假设 S S S的该位为1,那么可得

a n s 2 = { k t h r s o n u , i r s o n u ∃   & & s z r s o n u ≤ i 2 30 − d + k t h l s o n u , i − s z r s o n u e l s e ans2=\begin{cases} kth_{rson_u,i} \quad\quad\quad rson_u \exist \ \&\&sz_{rson_u} \le i \\ 2^{30-d}+kth_{lson_u,i-sz_{rson_u}} \quad else \end{cases} ans2={kthrsonu,irsonu &&szrsonui230d+kthlsonu,iszrsonuelse
那么
k t h u , i = min ⁡ ( a n s 1 , a n s 2 ) kth_{u,i}=\min(ans1,ans2) kthu,i=min(ans1,ans2)
这样上述就可以做到 O ( n l o g 值 域 ) O(nlog_{\text 值域}) O(nlog)预处理, O ( 1 ) O(1) O(1)查询。关于 k t h kth kth数组的空间大小,每个叶子结点会对从根到该叶子结点的路径上所有的点的第二维产生一个贡献,一共有 n n n个叶子结点,所以 k t h kth kth数组的空间是 O ( n l o g 值 域 ) O(nlog_{值域}) O(nlog)的。
回到本题,在上面那个子问题的基础上加上了 L , R L,R L,R的限制。在从高位到低位决策 S S S的过程中,一开始 L , R L,R L,R的对应位一定是一样的(或者没有这个阶段),在这个阶段我们只能把 S S S的该位确定为与 L , R L,R L,R的该位一样,没有发生决策,只需要根据按照找第 K K K小的逻辑走进对应的子树即可;然后到达某个位时, L L L的该位为0, R R R的该位为1,这时候我们就需要决策了:
(假设当前决策的位为第 d d d位)
(1)假设将 S S S的该位确定为0,那么接下来的上下界限制就变成了 [ l o w L , d , 2 31 − d ) [low_{L,d},2^{31-d}) [lowL,d,231d)(其中 l o w L , d low_{L,d} lowL,d表示从 L L L的第 d d d位作为最高位和其他的低位构成的数),相当于只有下界了,接下来的决策过程就要发生变化:如果 L L L该位为1,那么 S S S的该位只能确定为1;如果 L L L该位为0,那么如果 S S S的该位取0,即贴着下界,就往对应的子树走接着进行同样的决策过程(因为之后还是只有下界的状态),如果 S S S该位取1,那么接下来的决策过程就变成了无界了,那么就可以不用往下走,直接用上面的子问题中处理出的 k t h kth kth数组得到结果,比较两种决策结果,就能得到最优的决策。因为我们每次只会往一边走,所以这一部分的复杂度是 O ( l o g 值域 ) O(log_{\text{值域}}) O(log值域)的。
(2)假设将 S S S的该位确定为1,跟(1)类似,变成了只有上界。

按照上述过程,我们就可以得到询问结果了。

复杂度: O ( ( n + q ) l o g 值 域 ) O((n+q)log_{值域}) O((n+q)log)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct Trie_01{
	int ch[maxn*32][2],sz[maxn*32],val[maxn*32];
	vector<int>kth[maxn*32];
	int tot;
	void init(){
		tot=0;
		ch[0][0]=ch[0][1]=0;
	}
	int newnode(){
		++tot;
		ch[tot][0]=ch[tot][1]=0;
		kth[tot].clear();
		val[tot]=0;
		return tot;
	}
	void ins(int x){
		int now=0;
		for(int i=29;i>=0;i--){
			int d=(x>>i)&1;
			if(!ch[now][d]){
				ch[now][d]=newnode();
			}
			now=ch[now][d];
			val[now]++;
		}
	}
	void dfs(int u){
		if(!ch[u][0]&&!ch[u][1]){
			sz[u]=val[u];
			return;
		}
		if(ch[u][0]){
			dfs(ch[u][0]);
			sz[u]+=sz[ch[u][0]];
		}
		if(ch[u][1]){
			dfs(ch[u][1]);
			sz[u]+=sz[ch[u][1]];
		}
	}
	void dfs2(int u,int d){
		if(!ch[u][0]&&!ch[u][1]){
			kth[u].resize(val[u]+1);
			for(int i=1;i<=val[u];i++){
				kth[u][i]=0;
			}
			return;
		}
		if(ch[u][0]){
			dfs2(ch[u][0],d-1);
		}
		if(ch[u][1]){
			dfs2(ch[u][1],d-1);
		}
		kth[u].resize(sz[u]+1);
		for(int i=1;i<=sz[u];i++){
			int ans1,ans2;
			//0
			int lnum=0;
			if(ch[u][0]){
				lnum=sz[ch[u][0]];
			}
			if(lnum>=i){
				ans1=kth[ch[u][0]][i];
			}
			else{
				ans1=(1<<d)+kth[ch[u][1]][i-lnum];
			}

			//1
			lnum=0;
			if(ch[u][1]){
				lnum=sz[ch[u][1]];
			}
			if(lnum>=i){
				ans2=kth[ch[u][1]][i];
			}
			else{
				ans2=(1<<d)+kth[ch[u][0]][i-lnum];
			}
			kth[u][i]=min(ans1,ans2);
		}
	}
	int dfs3(int u,int d,int l,int k){
		if(!ch[u][0]&&!ch[u][1])return 0;
		int bit=(l>>d)&1;
		if(bit==1){
			int lnum=0;
			if(ch[u][1]){
				lnum=sz[ch[u][1]];
			}
			if(lnum>=k){
				return dfs3(ch[u][1],d-1,l,k);
			}
			else{
				return (1<<d)+dfs3(ch[u][0],d-1,l,k-lnum);
			}
		}	
		else{
			int ans1,ans2;
			int lnum=0;
			//0
			if(ch[u][0]){
				lnum=sz[ch[u][0]];
			}
			if(lnum>=k){
				ans1=dfs3(ch[u][0],d-1,l,k);
			}
			else{
				ans1=(1<<d)+dfs3(ch[u][1],d-1,l,k-lnum);
			}
			//1
			lnum=0;
			if(ch[u][1]){
				lnum=sz[ch[u][1]];
			}
			if(lnum>=k){
				ans2=kth[ch[u][1]][k];
			}
			else{
				ans2=(1<<d)+kth[ch[u][0]][k-lnum];
			}
			return min(ans1,ans2);
		}
	}
	int dfs4(int u,int d,int r,int k){
		if(!ch[u][0]&&!ch[u][1])return 0;
		int bit=(r>>d)&1;
		if(bit==0){
			int lnum=0;
			if(ch[u][0]){
				lnum=sz[ch[u][0]];
			}
			if(lnum>=k){
				return dfs4(ch[u][0],d-1,r,k);
			}
			else{
				return (1<<d)+dfs4(ch[u][1],d-1,r,k-lnum);
			}
		}	
		else{
			int ans1,ans2;
			int lnum=0;
			//0
			if(ch[u][0]){
				lnum=sz[ch[u][0]];
			}
			if(lnum>=k){
				ans1=kth[ch[u][0]][k];
			}
			else{
				ans1=(1<<d)+kth[ch[u][1]][k-lnum];
			}
			//1
			lnum=0;
			if(ch[u][1]){
				lnum=sz[ch[u][1]];
			}
			if(lnum>=k){
				ans2=dfs4(ch[u][1],d-1,r,k);
			}
			else{
				ans2=(1<<d)+dfs4(ch[u][0],d-1,r,k-lnum);
			}
			return min(ans1,ans2);
		}
	}
	int solve(int u,int d,int l,int r,int k){
		if(!ch[u][0]&&!ch[u][1])return 0;
		int lbit=(l>>d)&1,rbit=(r>>d)&1;
		if(lbit==rbit){
			int lnum=0;
			int zero=lbit;
			if(ch[u][zero]){
				lnum=sz[ch[u][zero]];
			}
			if(lnum>=k){
				return solve(ch[u][zero],d-1,l,r,k);
			}
			else{
				return (1<<d)+solve(ch[u][zero^1],d-1,l,r,k-lnum);
			}
		}
		else{
			int ans1,ans2;
			//0
			int lnum=0;
			if(ch[u][0]){
				lnum=sz[ch[u][0]];
			}
			if(lnum>=k){
				ans1=dfs3(ch[u][0],d-1,l,k);
			}
			else{
				ans1=(1<<d)+dfs3(ch[u][1],d-1,l,k-lnum);
			}

			//1
			lnum=0;
			if(ch[u][1]){
				lnum=sz[ch[u][1]];
			}
			if(lnum>=k){
				ans2=dfs4(ch[u][1],d-1,r,k);
			}
			else{
				ans2=(1<<d)+dfs4(ch[u][0],d-1,r,k-lnum);
			}
			return min(ans1,ans2);
		}
	}
}tree;
int n,q;
int a[maxn];
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&q);
	tree.init();
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		tree.ins(a[i]);
	}
	tree.dfs(0);
	tree.dfs2(0,29);
	int l,r,k;
	while(q--){
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",tree.solve(0,29,l,r,k));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值