CF241B Friends

Link
先考虑求出第(k)大的(a_ioperatorname{xor}a_j),可以在建出Trie树之后高位贪心。
然后需要求出比这个数大的和。
在Trie树上额外维护(c_{u,i})表示在(u)的子树内有多少个数的第(i)位为(1)
在高位贪心的时候,如果当前位能够凑到的(1)的数量比(k)大,那么这一位的取值就是(0),此时我们利用额外维护的信息计算前(k)大中有多少个这一位为(1)

#include<cstdio>
using i64=long long;
const int N=50007,M=30;
int tot=1,a[N],p[N];
struct node{int size,ch[2],c[M];}t[N*16];
int read(){int x;scanf("%d",&x);return x;}
void insert(int x)
{
    for(int i=29,p=1,d;~i;--i)
    {
	d=x>>i&1,++t[p=t[p].ch[d]? t[p].ch[d]:t[p].ch[d]=++tot].size;
	for(int j=0;j<30;++j) t[p].c[j]+=x>>j&1;
    }
}
int main()
{
    int n=read();i64 m=2ll*read(),ans=0,base=0;
    for(int i=1;i<=n;++i) insert(a[i]=read()),p[i]=1;
    for(int i=29;m&&~i;--i)
    {
	i64 s=0;
	for(int j=1;j<=n;++j) s+=t[t[p[j]].ch[!(a[j]>>i&1)]].size;
	if(s<=m)
	{
	    for(int j=1,q,d;d=a[j]>>i&1,q=p[j],j<=n;p[j++]=t[q].ch[d])
		if(q) for(int k=0;k<30;++k) ans+=1ll*(a[j]>>k&1? t[t[q].ch[!d]].size-t[t[q].ch[!d]].c[k]:t[t[q].ch[!d]].c[k])<<k;
	    m-=s;
	}
	else for(int j=1;j<=n;++j) p[j]=t[p[j]].ch[!(a[j]>>i&1)],base|=1<<i;
    }
    printf("%lld",(ans+m*base)/2%1000000007);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12782365.html