您的位置:首页 > 其它

[51nod1150] Logarithm

2016-12-16 22:21 183 查看

题目大意

给定一个n个正整数的数组A[],求∑i≠jlg(A[i]xorA[j])

n≤50000 1≤A[i]≤1018

分析

首先每个数不会很大,那么任意一个异或起来的数的lg值不会超过18

然后可以考虑lg的值,假设是x,然后可以确定一个区间[10x,10x+1),如果异或值在这个区间内,它对答案的贡献就是x。

然后就是求多少个异或值在一个区间内,做法就是套路:可以先把所有数放进二进制trie里,然后枚举每个数,在trie上跑即可。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=50005,M=N*62;

typedef unsigned long long LL;

int n,tot,son[M][2],cnt[M];

LL Ans
,A
,s,p
;

char c;

LL read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
LL x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}

void insert(LL x,int y,int z)
{
if (y<0) return;
int w=((x & p[y])>0);
if (!son[z][w]) son[z][w]=++tot;
cnt[son[z][w]]++;
insert(x,y-1,son[z][w]);
}

int get(LL x,LL k,int y,int z)
{
if (y<0 || !z) return cnt[z];
int w=((x & p[y])>0);
if ((k & p[y])==0) return get(x,k,y-1,son[z][w])+cnt[son[z][1-w]];
return get(x,k,y-1,son[z][1-w]);
}

int main()
{
scanf("%d",&n);
p[0]=1;
for (int i=1;i<=62;i++) p[i]=p[i-1]*2;
tot=1;
for (int i=0;i<n;i++)
{
A[i]=read();
insert(A[i],61,1);
}
for (LL i=1,t=10;t<=1e18;i++,t*=10)
{
Ans[i]=(LL)n*(n-1);
for (int j=0;j<n;j++) Ans[i]-=get(A[j],t,61,1);
if (i>0) s+=(i-1)*(Ans[i]-Ans[i-1]);
if (t==1e19) break;
}
s+=((LL)n*(n-1)-Ans[18])*18;
printf("%lld\n",s);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: