CF1466E Apollo versus Pan 题解


本题是一道数学题。

我们首先需要交换一下求和符号,然后利用二进制的性质拆式子即可。

具体过程如下:

\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1}^{n}(x_i \text{ and }x_j) \times (x_j \text{ or } x_k)\)

\(=\sum\limits_{j=1}^{n}\sum\limits_{i=1}^{n}\sum\limits_{k=1}^{n}(x_i \text{ and }x_j) \times (x_j \text{ or } x_k)\)

\(=\sum\limits_{j=1}^{n}\sum\limits_{i=1}^{n}(x_i \text{ and } x_j) \times \sum\limits_{k=1}^{n}(x_j \text{ or } x_k)\)

\(=\sum\limits_{j=1}^{n}(\sum\limits_{i=1}^{n}(x_i \text{ and } x_j) \times \sum\limits_{k=1}^{n}(x_j \text{ or } x_k))\)

于是我们只需要按二进制位拆分,然后处理即可。

代码:

#include 
using namespace std;

typedef long long LL;
const int MAXN = 5e5 + 10, P = 1e9 + 7;
int t, n;
LL x[MAXN], ans, sum[MAXN];

LL read()
{
	LL sum = 0, fh = 1; char ch = getchar();
	while (ch < '0' || ch > '9')  {if (ch == '-') fh = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
	return sum * fh;
}

int main()
{
	t = read();
	while (t--)
	{
		n = read(); ans = 0;
		for (int i = 1; i <= n; ++i) x[i] = read();
		memset(sum, 0, sizeof(sum));
		for (int i = 1; i <= n; ++i)
			for (int j = 0; j < 60; ++j)
				if (1ll << j & x[i]) sum[j]++;
		for (int i = 1; i <= n; ++i)
		{
			LL sum1 = 0, sum2 = 0;
			for (int j = 0; j < 60; ++j)
			{
				LL tmp = (1ll << j) % P;
				if (1ll << j & x[i]) sum1 = (sum1 + sum[j] * tmp % P) % P, sum2 = (sum2 + n * tmp) % P;
				else sum2 = (sum2 + sum[j] * tmp) % P;
			}
			ans = (ans + sum1 * sum2 % P) % P;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

相关