HDU 5269 ZYB loves Xor I [01字典树]【思维】

66 篇文章 0 订阅
10 篇文章 0 订阅

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5269
—————————————————————————————-.
ZYB loves Xor I Accepts: 142 Submissions: 696
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
问题描述
ZYB喜欢研究Xor,现在他得到了一个长度为nn的数组A。于是他想知道:对于所有数对 (i,j)(i[1,n],j[1,n]) lowbit(AixorAj)lowbit(AixorAj)
之和为多少.由于答案可能过大,你需要输出答案对998244353取模后的值
定义 lowbit(x)=2k
​​ ,其中k是最小的满足 (xand2k)>0 的数
特别地:lowbit(0)=0

输入描述
一共 (T10) 组数据,对于每组数据:
第一行一个正整数n,表示数组长度
第二行n个非负整数,第 i 个整数为Ai
n[1,5104],Ai[0,229]

输出描述
每组数据输出一行Case #x: ans。x表示组数编号,从1开始。ans为所求值。

输入样例
2
5
4 0 2 7 0
5
2 6 5 4 0

输出样例
Case #1: 36
Case #2: 40
—————————————————————————————-.
解题思路:

这道题首先想的是用数组处理二进制每一位的0,1的个数,然后进行统计,在处理一下细节,但是最后发现只有在处理二进制下最后一位为1的数才好统计.于是GG

最后看了题解,提到了字典树,,,,顿时茅塞顿开,

我们只要从低位开始插入字典树中,并统计个数,每次统计就是二进制位上和它相反的数的个数* (1<<i) ,

边插入边统计就好了.

附本题代码
—————————————————————————————-.

#include <bits/stdc++.h>
using namespace std;

#define INF        (~(1<<31))
#define INFLL      (~(1ll<<63))
#define pb         push_back
#define mp         make_pair
#define abs(a)     ((a)>0?(a):-(a))
#define lalal      puts("*******");
#define s1(x)      scanf("%d",&x)
#define Rep(a,b,c) for(int a=(b);a<=(c);a++)
#define Per(a,b,c) for(int a=(b);a>=(c);a--)
#define no         puts("NO")

typedef long long int LL ;
typedef unsigned long long int uLL ;

const int    MOD = 998244353;
const int    N   = 1500000+5;
const double eps = 1e-6;
const double PI  = acos(-1.0);
/***********************************************************************/

int trie[N][2],val[N],cnt;

void trieinsert(int x){
    int now = 0;
    for(int i=0;i<30;x>>=1,++i){
        if(!trie[now][x%2]) trie[now][x%2]=++cnt;
        now = trie[now][x%2];
        val[now]++;
    }
}

LL query(int x){
    int now = 0;
    LL ans =0ll;
    for(int i=0;i<30;x>>=1,++i){
        if(!trie[now][x%2]) trie[now][x%2]=++cnt;
        ans += (2ll<<i)*val[trie[now][1-x%2]];
        ans%=MOD;
        now = trie[now][x%2];
        val[now]++;
    }
    return ans ;
}

int main(){
    int _ = 1,kcase ;
    while(~scanf("%d",&_)){
        kcase = 0;
        while(_--){

            int n;
            scanf("%d",&n);

            cnt = 0;

            LL x;
            LL ans = 0ll;
            for(int i=0;i<n;i++){
                scanf("%I64d",&x);
                ans+=query(x);
                ans%=MOD;
                //printf("%I64d\n",ans);
                //trieinsert(x);
            }
            for(int i=0;i<=cnt;i++){
                trie[i][0]=trie[i][1]=val[i]=0;
            }
            printf("Case #%d: %I64d\n",++kcase,ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值