[51nod1150] Logarithm

题目大意

给定一个n个正整数的数组A[],求 ijlg(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[N],A[N],s,p[N];

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值