ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::
题目来源: Ural 1318
给出n个互不相等的整数A[0] - A[n-1],选A[i]同A[j]进行异或运算(结果都 > 0无符号),对结果取lg(以10为底)并取整后记为L[i,j],求n个数之间两两运算得到的L[i,j]之和。
 
Problem illustration
 
例如:1 10 30,1 xor 10 = 11,10 xor 30 = 20,1 xor 30 = 31。Sum = Lg(11) * 2 + Lg(20) * 2 + Lg(30) * 2 = (1 + 1 + 1) * 2 = 6(取整)。
由于i j同j i算作2个不同的,因此最终结果要 * 2。
Input
第1行:1个数N,表示整数的数量。(2 <= N <= 50000)
第2 - N + 1行:每行个1数A[i](1 <= A[i] <= 10^18)
Output
输出两两异或后取Lg(以10为底)并取整后的和。

首先建出二进制trie,然后由数据范围知log10(A[i] xor A[j])在0到19,可以枚举每个数和log10的每个取值在trie上查询出现次数
#include<cstdio>
typedef long long i64;
i64 _(){
    i64 x=0;
    int c=getchar();
    while(c<48)c=getchar();
    while(c>47)x=x*10+c-48,c=getchar();
    return x;
}
const int N=50007*65;
int n;
i64 a[50007];
int ch[N][2],sz[N],ptr=1;
i64 ts[20],p10[20];
void ins(i64 x){
    int w=1;
    for(int i=62;~i;i--){
        int d=x>>i&1;
        if(!ch[w][d])ch[w][d]=++ptr;
        w=ch[w][d];
        ++sz[w];
    }
}
int find(i64 x,i64 y){
    int s=0,w=1;
    for(int i=62;~i;i--){
        int d1=x>>i&1,d2=y>>i&1;
        if(d2)s+=sz[ch[w][d1^d2^1]];
        w=ch[w][d1^d2];
    }
    return s;
}
int main(){
    n=_();
    for(int i=0;i<n;i++){
        a[i]=_();
        ins(a[i]);
    }
    p10[0]=1;
    for(int i=1;i<=18;i++)p10[i]=p10[i-1]*10;
    for(int i=0;i<n;i++){
        for(int j=1;j<=18;j++)ts[j]+=find(a[i],p10[j]);
    }
    ts[19]=1ll*n*n-ts[18];
    for(int i=18;i>1;i--)ts[i]-=ts[i-1];
    ts[1]-=n;
    i64 ans=0;
    for(int i=2;i<=19;i++)ans+=(i-1)*ts[i];
    printf("%lld",ans);
    return 0;
}

 

 
posted on 2016-08-17 23:31  nul  阅读(307)  评论(0编辑  收藏  举报