Good Bye 2020 E. Apollo versus Pan(位运算)

题目描述

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

题目大意

计算公式: ∑ 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])的值。

题目分析

在这个题中,我们要做的就是将O(n3)的公式优化到O(n)。
先来看看如何优化到O(n2):

原 式 原式
= ∑ 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])

经过了一系列的变形,我们就可以将其优化到O(n2)了。

要再将其优化到O(n)就需要运用位运算的性质了:

  1. 首先我们可以统计一下这n个数的二进制形式中,都是那些位上是1(这一步可以预处理出来)cnt[i] //表示这n个数中有多少个数的二进制在第i位上是1
  2. 枚举这n个数(j的那一层循环),再枚举二进制的每一位数(本题中一共是60位,用i表示)。
  3. 如果a[j]在第i位上是1,那么&的这一部分的值为 c n t [ i ] ∗ ( 1 < < i ) cnt[i]*(1<<i) cnt[i](1<<i),而 | 的这一部分的值为 n ∗ ( 1 < < i ) n*(1<<i) n(1<<i)
  4. 如果a[j]在第i位上是0,那么&的这一部分的值为0,而 | 的这一部分的值为 c n t [ i ] ∗ ( 1 < < i ) cnt[i]*(1<<i) cnt[i](1<<i)
  5. 算出所有位置上&和 | 的和之后相乘,即可得到 ( ∑ i = 1 n a [ 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]) 的值。这样就能将O(n)的时间复杂度优化到O(60)。最终的时间复杂度即为O(60*n)。
代码如下
#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;
}

猜你喜欢

转载自blog.csdn.net/li_wen_zhuo/article/details/113929804