CF241B Friends
看到两两异或,考虑 01-Trie。
发现直接取出前 \(m\) 大的异或和非常不好做!于是考虑求第 \(m\) 大的异或和是多少。二分一只 \(\log\),然后对每个数求异或它结果大于 \(m\) 的数,可以直接在 Trie 上数,一只 \(\log\)。所以单次 check 是 \(O(n\log n)\),这一步复杂度是 \(O(n\log^2 n)\)。
然后我们发现有第 \(m\) 大的异或和 \(k\),那么就要求所有大于它的异或和之和。我们惊奇的发现这就是一次 check,然后就写完了。
注意有相等的情况,所以我们求的是异或和 \(>k\) 的部分,剩下的再用 \(k\) 补齐。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7,inv2=5e8+4;
int n,ans,k,a[50005];
int ch[1000005][2],val[1000005],cnt;
void ins(int x){
int cur=0;
for(int i=30;i>=0;i--){
int t=((1<<i)&x)!=0;
if(!ch[cur][t])ch[cur][t]=++cnt;
cur=ch[cur][t];val[cur]++;
}
}
int check(int x){
int tot=0;
for(int i=1;i<=n;i++){
int cur=0;
for(int j=30;j>=0;j--){
int t1=(a[i]>>j)&1,t2=(x>>j)&1;
if(!t2)tot+=val[ch[cur][t1^1]],cur=ch[cur][t1];
else cur=ch[cur][t1^1];
if(!cur)break;
}
tot+=val[cur];
}
return tot/2;
}
int tr[1000005][35];
void pre(int x,int dep,int z){
if(!x)return;
if(!dep){
for(int i=0;i<=30;i++)if((1<<i)&z)tr[x][i]=val[x];
return;
}
pre(ch[x][0],dep-1,z);
pre(ch[x][1],dep-1,z|(1<<dep-1));
for(int i=0;i<=30;i++)tr[x][i]=tr[ch[x][0]][i]+tr[ch[x][1]][i];
}
void solve(int K){
for(int i=1;i<=n;i++){
int cur=0;
for(int j=30;j>=0;j--){
int t1=(a[i]>>j)&1,t2=(K>>j)&1;
if(!t2){
int t=ch[cur][t1^1];
for(int k=0;k<=30;k++){
int t3=((1<<k)&a[i])!=0;
if(t3)ans=(ans+(val[t]-tr[t][k])*(1<<k))%mod;
else ans=(ans+tr[t][k]*(1<<k))%mod;
}
cur=ch[cur][t1];
}
else cur=ch[cur][t1^1];
if(cur==0)break;
}
ans=(ans+val[cur]*K)%mod;
}
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i],ins(a[i]);
int l=0,r=1<<30,K=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)>=k)K=mid,l=mid+1;
else r=mid-1;
}
pre(ch[0][0],30,0);
pre(ch[0][1],30,1<<30);
solve(K);
cout<<((ans*inv2%mod-(check(K)-k)*K%mod)%mod+mod)%mod<<endl;
return 0;
}

浙公网安备 33010602011771号