其他分享
首页 > 其他分享> > CF241B Friends

CF241B Friends

作者:互联网

CF241B Friends

题意简述:给出序列 \(a_{[1,n]}\) 和 \(m\),求出前 \(m\) 大的 \(a_i\oplus a_j\ (1\leq i<j\leq n)\) 的和。对 \(10^9+7\) 取模。

\(1\leq n\leq 5\times 10^4\),\(0\leq m\leq \binom{n}{2}\),\(0\leq a_i\leq 10^9\)。

P5283 的加强版,不过复杂度稍劣了些。

具体来说,我们可以二分找到第 \(k\) 大的异或和 \(v\),再求出所有不小于 \(v\) 的异或和的和。

首先,判断异或和 \(\geq mid\) 的点对数量可以通过可持久化 Trie 来解决:Trie 上的每个节点维护落在该区间的数的个数。枚举 \(i\),在 \(T_n-T_i\) 这个 Trie 上查询。设 \(a_i\) 和 \(mid\) 当前位分别为 \(c,t\),当前区间在 \(T_i\) 和 \(T_n\) 上的编号分别为 \(x,y\)。若 \(t=0\) 表示当前位为 \(c\oplus 1\) 的数都要被计算在内,则答案加上 \(val_{son_{y,c\oplus 1}}-val_{son_{x,c\oplus 1}}\)。然后向 \(c\oplus t\) 儿子方向搜索即可。该部分时间复杂度 \(n\log^2 w\),空间复杂度 \(n\log w\),其中 \(w\) 是值域。

所有不小于 \(v\) 的异或和的和比较棘手。仍然枚举 \(i\),因为 \((x+y)\oplus z\) 并不一定等于 \((x\oplus z)+(y\oplus z)\),所以不能单纯维护落在该区间的数的和。但是我们可以对每一位单独考虑:\(s_{x,i}\) 表示值落在 \(x\) 所表示区间的数有 \(s_{x,i}\) 个第 \(i\) 位为 \(1\)。查询时类似上面一部分,若 \(t=0\) 则再枚举位数 \(b\),若 \(a_i\) 的第 \(b\) 位为 \(0\),则要记入第 \(b\) 位为 \(1\) 的数的个数,即为 \(s_{y,b}-s_{x,b}\),反之亦然,个数为 \(val_y-val_x-(s_{y,b}-s_{x,b})\)。注意这里的 \(x,y\) 其实应该是上一部分中的 \(son_{x,c\oplus 1},son_{y,c\oplus 1}\)。该部分时间复杂度 \(n\log^2 w\),空间复杂度 \(n\log^2 w\)。

当然,这个解法仍有可以优化的地方:第一部分可以直接在 Trie 树上二分。设 \(a_i\) 当前位为 \(c_i\),判断 \(\sum_{i=1}^{n-1}val_{son_{y,c_i\oplus 1}}-val_{son_{x,c_i\oplus 1}}\)(记为 \(cnt\))是否不小于 \(m\),即 \(a_i\oplus a_j\) 的当前位为 \(1\) 的总方案数。若 \(m\leq cnt\) 则表明第 \(m\) 大的异或和当前位为 \(1\),反之为 \(0\) 且将 \(m\) 减去 \(cnt\),再向根据判断结果向对应方向继续查询即可。这样时间复杂度为 \(n\log w\),可惜第二部分的时间复杂度无法优化。

空间复杂度的优化可以看其它题解。

注意开 long long,并且二分边界应该设为 \(2^{30}\) 而不是单纯的 \(10^9\)。

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int N=5e4+5;
const int p=1e9+7;
const int B=30;

int n,m,a[N];
int node,R[N],ls[N<<5],rs[N<<5],val[N<<5],c[N<<5][B];
void modify(int pre,int &x,int p,int b){
	val[x=++node]=val[pre]+1,ls[x]=ls[pre],rs[x]=rs[pre];
	for(int i=0;i<B;i++)c[x][i]=c[pre][i]+(p>>i&1);
	if(b==0)return;
	if((p>>b-1)&1)modify(rs[pre],rs[x],p,b-1);
	else modify(ls[pre],ls[x],p,b-1);
}
ll query(int p,int k,int b,int x,int y){
	if(b==0)return val[y]-val[x];
	int bit=(p>>b-1)&1,t=(k>>b-1)&1;
	if(bit){
		if(t)return query(p,k,b-1,ls[x],ls[y]);
		else return val[ls[y]]-val[ls[x]]+query(p,k,b-1,rs[x],rs[y]);
	} 
	else{
		if(t)return query(p,k,b-1,rs[x],rs[y]);
		else return val[rs[y]]-val[rs[x]]+query(p,k,b-1,ls[x],ls[y]);
	}
}

ll cal(int x,int y,int p){
	ll ans=0;
	for(ll i=0;i<B;i++)
		if((p>>i)&1)ans+=(ll)(val[y]-val[x]-c[y][i]+c[x][i])<<i;
		else ans+=(ll)(c[y][i]-c[x][i])<<i;
	return ans;
}
ll sum(int p,int k,int b,int x,int y){
	if(b==0)return cal(x,y,p);
	int bt=(p>>b-1)&1,t=(k>>b-1)&1;
	if(bt){
		if(t)return sum(p,k,b-1,ls[x],ls[y]);
		else return cal(ls[x],ls[y],p)+sum(p,k,b-1,rs[x],rs[y]);
	}
	else{
		if(t)return sum(p,k,b-1,rs[x],rs[y]);
		else return cal(rs[x],rs[y],p)+sum(p,k,b-1,ls[x],ls[y]);
	}
}

int main(){
	cin>>n>>m;
	if(m==0)return cout<<0<<endl,0;
	for(int i=1;i<=n;i++)cin>>a[i],modify(R[i-1],R[i],a[i],B);
	ll l=0,r=1<<B,ans=0,cnt=0;
	while(l<r){
		ll mid=(l+r>>1)+1,cnt=0;
		for(int i=1;i<=n;i++)cnt=cnt+query(a[i],mid,B,R[i],R[n]);
		cnt>=m?l=mid:r=mid-1;
	}
	for(int i=1;i<n;i++)cnt=cnt+query(a[i],l,B,R[i],R[n]);
	for(int i=1;i<n;i++)ans+=sum(a[i],l,B,R[i],R[n]);
	cout<<(ans-(cnt-m)*l%p+p)%p<<endl;
	return 0;
}

标签:return,val,rs,int,ls,CF241B,Friends,oplus
来源: https://www.cnblogs.com/alex-wei/p/CF241B.html