题目描述:给定一个数列{AnA_nAn},考虑所有数对(i,j)(i,j)(i,j),(i∈[1,n],j∈[1,n])(i\in[1,n],j\in[1,n])(i∈[1,n],j∈[1,n]),求所有的lowbit(A[i],A[j])lowbit(A[i],A[j])lowbit(A[i],A[j])之和
答案对199907210507199907210507199907210507取模
输入格式:第一行一个整数nnn
---------------第二行nnn个整数,代表AAA数组
输出格式:一个数,所有的lowbit(A[i],A[j])lowbit(A[i],A[j])lowbit(A[i],A[j])之和
数据范围:1<=n<=1051 <= n <= 10^51<=n<=105
1<=A[i]<=260−1\quad\quad\quad\quad \ 1<=A[i] <=2^{60}-1 1<=A[i]<=260−1
考虑到lowbit(A[i],A[j])lowbit(A[i],A[j])lowbit(A[i],A[j])的含义为A[i],A[j]A[i],A[j]A[i],A[j]二进制拆分下的最小一位不同的位置所代表的不同的二进制数, (考后听同学讲述了01trie01trie01trie之后) ,等价于他们有一段公共前缀,就可以自然而然地想到01trie01trie01trie。
建tiretiretire,每个数从低位到高位插入,这一位是000就进入该树的左子树,否则进入右子树,记住每一个数都要有606060长度,高位补000,否则不好计数
设nnn代表A[i],A[j]A[i],A[j]A[i],A[j]在trietrietrie的公共前缀的长度
建好树后,因为lowbit(A[i],A[j])lowbit(A[i],A[j])lowbit(A[i],A[j])等于2n2^n2n,dfs trie树dfs \ trie树dfs trie树,对于每一个节点,ansansans加上 左子树的个数与右子树个数和2n2^n2n(nnn为深度)这三个数的乘积即可
这也是为什么每个数插入的时候都要高位补000
(trietrietrie在解决这种带有公共前缀的问题有奇用)
CodeCodeCode
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5, Mod = 199907210507;
int a[MAXN + 10];
int tree[MAXN * 70 + 10][2], size[MAXN * 70 + 10], rt = 0, tot = 0, ans = 0;
inline int read();
namespace subtask1{
inline int lowbit(int x){return x&(-x);}
void Main(int n){
int ans = 0;
for (register int i = 1; i <= n; ++i)
for (register int j = i + 1; j <= n; ++j)
ans = (ans + 2 * lowbit(a[i]^ a[j])) % Mod;
printf("%lld\n",ans);
}
}
void dfs(int x, int sum){
if (tree[x][0] != -1) dfs(tree[x][0], sum * 2 % Mod);
if (tree[x][1] != -1) dfs(tree[x][1], sum * 2 % Mod);
if (tree[x][0] < 0) tree[x][0] = tot + 1;
if (tree[x][1] < 0) tree[x][1] = tot + 1;
ans = (ans + size[tree[x][0]] * sum % Mod * size[tree[x][1]] % Mod) % Mod;
size[x] += size[tree[x][0]] + size[tree[x][1]];
}
signed main(){
freopen ("xor.in","r",stdin);
freopen ("xor.out","w",stdout);
memset(tree, -1 ,sizeof(tree));
int n = read(), mx = 0;
for (register int i = 1; i <= n; ++i){
a[i]= read();
mx = max(mx, a[i]);
}
if (n <= 200){
subtask1::Main(n);
return 0;
}
for (register int i = 1; i <= n; ++i){
int now = rt;
for (register int j = 1; j <= mx; j = j * 2){
int x = (a[i] & j) ? 1 : 0;
if (tree[now][x] == -1) tree[now][x] = ++tot;
now = tree[now][x];
}
++size[now];
}
dfs(0, 1);
printf("%lld\n", ans * 2 % Mod);
return 0;
}
inline int read(){
int x = 0;
char c = getchar();
while (!isdigit(c))c = getchar();
while (isdigit(c))x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return x;
}

博客介绍了如何利用01 trie数据结构解决数对低bit(A[i], A[j])之和的问题。通过建立01 trie树,计算数列中所有数对的公共前缀长度,进而求得lowbit之和,答案对199907210507取模。题目要求1<=n<=105, 1<=A[i]<=2^60-1,文章提供了解题思路及代码实现。"
126742029,15292924,链表在Java开发中的应用实践,"['数据结构', '链表', 'Java开发', '算法应用']
476

被折叠的 条评论
为什么被折叠?



