Good Bye 2020 E. Apollo versus Pan (bit operation)

Title description

Only a few know that Pan and Apollo weren’t only battling for the title of the GOAT musician. A few millenniums later, they also challenged each other in math (or rather in fast calculations). The task they got to solve is the following:
Let x1,x2,…,xn be the sequence of n non-negative integers. Find this value:
∑i=1n∑j=1n∑k=1n(xi&xj)⋅(xj|xk)
Here & denotes the bitwise and, and | denotes the bitwise or.
Pan and Apollo could solve this in a few seconds. Can you do it too? For convenience, find the answer modulo 109+7.

Input

The first line of the input contains a single integer t (1≤t≤1000) denoting the number of test cases, then t test cases follow.
The first line of each test case consists of a single integer n (1≤n≤5⋅105), the length of the sequence. The second one contains n non-negative integers x1,x2,…,xn (0≤xi<260), elements of the sequence.
The sum of n over all test cases will not exceed 5⋅105.

Output

Print t lines. The i-th line should contain the answer to the i-th text case.

Example

input
8
2
1 7
3
1 2 4
4
5 5 5 5
5
6 2 2 1 0
1
0
1
1
6
1 12 123 1234 12345 123456
5
536870912 536870911 1152921504606846975 1152921504606846974 1152921504606846973
output
128
91
1600
505
0
1
502811676
264880351

General idea

计算公式: ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( a [ i ] \displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^{n}\displaystyle\sum_{k=1}^{n}(a[i] i=1nj=1nk=1n(a[i]& a [ j ] ) ∗ ( a [ j ] ∣ a [ k ] ) a[j])*(a[j]|a[k]) a[j])( a [ j ] a [ k ] ) value.

Topic analysis

In this question, what we have to do is to optimize the formula of O(n 3 ) to O(n).
Let's first look at how to optimize to O(n 2 ):

Original formula Original formula
= ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( a [ i ] \displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^{n}\displaystyle\sum_{k=1}^{n}(a[i] i=1nj=1nk=1n(a[i]& a [ j ] ) ∗ ( a [ j ] ∣ a [ k ] ) a[j])*(a[j]|a[k]) a[j])(a[j]a[k])
= ∑ j = 1 n ∑ i = 1 n ∑ k = 1 n ( a [ i ] \displaystyle\sum_{j=1}^{n}\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{k=1}^{n}(a[i] j=1ni=1nk=1n(a[i]& a [ j ] ) ∗ ( a [ j ] ∣ a [ k ] ) a[j])*(a[j]|a[k]) a[j])(a[j]a[k])
= ∑ j = 1 n ( ∑ i = 1 n a [ i ] \displaystyle\sum_{j=1}^{n}(\displaystyle\sum_{i=1}^{n}a[i] j=1n(i=1na[i]& a [ j ] ) ∗ ( ∑ k = 1 n a [ j ] ∣ a [ k ] ) a[j])*(\displaystyle\sum_{k=1}^{n}a[j]|a[k]) a[j])(k=1na[j]a[k])

After a series of transformations, we can optimize it to O(n 2 ).

To optimize it to O(n), you need to use the nature of bit operations:

  1. First of all, we can count the binary form of these n numbers, which bits are 1 (this step can be preprocessed)cnt[i] //表示这n个数中有多少个数的二进制在第i位上是1 .
  2. Enumerate these n numbers (the level of j in the loop), and then enumerate each digit of the binary (in this question, there are 60 digits in total, denoted by i).
  3. If a[j] is 1 in the ith position, then the value of this part of & is cnt [i] ∗ (1 <<i) cnt[i]*(1<<i)cnt[i](1<<i ) , and the value of this part of | isn ∗ (1 <<i) n*(1<<i)n(1<<i)
  4. If a[j] is 0 at the i-th position, then the value of this part of & is 0, and the value of this part of | is cnt [i] ∗ (1 <<i) cnt[i]*(1< <i)cnt[i](1<<i)
  5. Calculate the sums of & and | at all positions and multiply them together to get (∑ i = 1 na [i] (\displaystyle\sum_{i=1}^{n}a[i](i=1na[i]& a [ j ] ) ∗ ( ∑ k = 1 n a [ j ] ∣ a [ k ] ) a[j])*(\displaystyle\sum_{k=1}^{n}a[j]|a[k]) a[j])(k=1na [ j ] a [ k ] ) value. In this way, the time complexity of O(n) can be optimized to O(60). The final time complexity is O(60*n).
code show as below
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <unordered_map>
#include <queue>
#include <vector>
#include <set>
#include <bitset>
#include <algorithm>
#define LL long long
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int N=5e5+5,mod=1e9+7;
LL a[N],p[65];
int cnt[65];
int main()
{
    
    
	for(int i=0;i<61;i++) i?p[i]=p[i-1]*2%mod:p[0]=1;	//先预处理出2^i的值
	int t;
	scanf("%d",&t);
	while(t--)
	{
    
    
		memset(cnt,0,sizeof cnt);
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		for(int i=0;i<61;i++)				//预处理出这n个数中有多少个在第i位上的值是1
			for(int j=1;j<=n;j++)
				if((a[j]>>i)&1) cnt[i]++;
		
		LL ans=0;
		for(int j=1;j<=n;j++)			//枚举j层
		{
    
    
			LL t1=0,t2=0;				//t1记录&部分的和,t2记录|部分的和
			for(int i=0;i<61;i++)		//枚举二进制的60位
			{
    
    
				if((a[j]>>i)&1)			//a[j]在第i位上是1的情况
				{
    
    
					t1=(t1+cnt[i]*p[i])%mod;
					t2=(t2+n*p[i])%mod;
				}
				else t2=(t2+cnt[i]*p[i])%mod;		//a[j]在第i位上是0的情况
			}
			ans=(ans+t1*t2)%mod;
		}
		printf("%lld\n",ans);
	}
    return 0;
}

Guess you like

Origin blog.csdn.net/li_wen_zhuo/article/details/113929804
Recommended