#3315. fullcombo

题目描述

SmallBell最近沉迷Lovelive,苦于[Beat In Angel]的fc。他在研究用什么队伍的时候,顺便把队伍的Cool属性也记录了下来,于是他得到 $n$ 个数。

旁边的WerKeyTom_FTD正苦于如何卡常数。他突然对SmallBell提了个问题:选择两个数 $i,j$ $(i \neq j)$ ,把第 $i,j$ 个数异或起来,求第 $k$ 大的异或值是多少。注意: $(i,j)$ 和 $(j,i)$ 是不同方案。

SmallBell:好水啊,一下就切了!然后把问题交给了你。

数据范围

除 $k$ 外所有数为不大于 $5 \times 10^4$ 的正整数, $1 \le k \le n \times (n-1)$

题解

建立 $trie$ 树,考虑二分答案,然后每个数在 $trie$ 树上找出异或值 $≥mid$ 的个数总和,然后和 $k$ 判断即可,效率: $O(nlog^2n)$

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e4+5;
int n,a[N],l,r=(1<<16)-1,d,t=1;
LL k;
struct O{
    int ch[2],sz;
}tr[N*50];
void ins(int x){
    int p=1;
    for (int v,i=15;~i;i--){
        v=(x>>i)&1;
        if (!tr[p].ch[v])
            tr[p].ch[v]=++t;
        p=tr[p].ch[v];tr[p].sz++;
    }
}
int get(int x){
    int p=1,sum=0;
    for (int v,i=15;~i;i--){
        v=(x>>i)&1;
        if (d&(1<<i))
            p=tr[p].ch[v^1];
        else 
            sum+=tr[tr[p].ch[v^1]].sz,
            p=tr[p].ch[v];
    }
    return sum+tr[p].sz;
}
bool J(){
    LL sum=0;
    for (int i=1;i<=n;i++)
        sum+=1ll*get(a[i]);
    return (sum>=k);
}
int main(){
    scanf("%d%lld",&n,&k);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]),ins(a[i]);
    while(l<r){
        d=(l+r+1)>>1;
        if (J()) l=d;
        else r=d-1;
    }
    return printf("%d\n",l),0;
}

猜你喜欢

转载自www.cnblogs.com/xjqxjq/p/11327028.html
今日推荐