Good Bye 2020 E. Apollo versus Pan

原题传送门
相关题意请了解后再来看此篇题解 (绝不是偷懒

正篇

题目让我们求的是 ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i   &   x j ) ⋅ ( x j   ∣   x k ) \displaystyle \sum_{i=1}^n \sum_{j=1}^n \sum_{k=1}^n (x_i \, \& \, x_j) \cdot (x_j \, | \, x_k) i=1nj=1nk=1n(xi&xj)(xjxk)。这个式子看起来比较复杂,有三个变量,但是不难发现,这里变量 i \displaystyle i i和变量 j \displaystyle j j绑定,而变量 j \displaystyle j j又与变量 k \displaystyle k k绑定,且题目数据范围非常大,我们不太可能枚举两个这样的大范围变量,故考虑对和式化简,以便我们处理。注意到对每一个给定的 j \displaystyle j j n \displaystyle n n个值要跟它取或运算并求和,于是我们将和式进行第一步转化得到如下式子:
∑ i = 1 n ∑ j = 1 n ( x i   &   x j ) ∑ k = 1 n ( x j   ∣   x k ) \displaystyle \sum_{i=1}^n \sum_{j=1}^n(x_i \, \& \, x_j) \sum_{k=1}^n (x_j \, | \, x_k) i=1nj=1n(xi&xj)k=1n(xjxk)
但是光化简这一步,我们的和式依旧很难处理,这里的求和顺序是先给定一个 i \displaystyle i i,然后枚举 j \displaystyle j j在对每一个给定的 j \displaystyle j j k \displaystyle k k进行求和,形成了一个类似链一样的传递,我们不妨在思考一下,前面的两个和式意义是什么?其实就是枚举了 ( i , j ) \displaystyle (i,j) (i,j)对,那么我们是否可以考虑将 j \displaystyle j j转化为"最高优先级",对于每一个 i \displaystyle i i n \displaystyle n n j \displaystyle j j可以与其配对,那么反过来,对于每一个 j \displaystyle j j同样有 n \displaystyle n n i \displaystyle i i与其配对。于是和式又可以进一步转化:
∑ j = 1 n ( ∑ i = 1 n ( x i   &   x j ) ∑ k = 1 n ( x j   ∣   x k ) ) \displaystyle\sum_{j=1}^n (\sum_{i=1}^n (x_i \, \& \, x_j) \sum_{k=1}^n (x_j \, | \, x_k)) j=1n(i=1n(xi&xj)k=1n(xjxk))
对于这个式子,我们就相对好处理很多了,我们只需要能够快速求得 x j \displaystyle x_j xj与整个序列的"&“运算和,以及 x j \displaystyle x_j xj与整个序列的”|“运算和。由于都是位运算,整体考虑,肯定是很困难的,我们不妨把每一位拿出来考虑,毕竟最多也只有60位,枚举一下还是可以接受的。
我们单独思考每一位,一个数的二进制表示的某一位无非两种情况,要么是0,要么就是1。这样我们进行分类:
( 1 ) \displaystyle (1) (1)如果 x j \displaystyle x_j xj的第 p \displaystyle p p位是0,那么这一位的”&“运算一定是0,对答案无贡献,再考虑”|“运算,由于这一位是1,那么它或上任意值都是那个数本身,那么这一位的贡献便是所有数在这一位的贡献。即:
∑ i = 1 n ( x i p   &   x j p ) = 0 \displaystyle\sum_{i=1}^n (x_{ip}\, \& \, x_{jp})=0 i=1n(xip&xjp)=0
∑ k = 1 n ( x j p   ∣   x k p ) = ∑ i = 1 n x i p \displaystyle\sum_{k=1}^n (x_{jp} \, | \, x_{kp})=\sum_{i=1}^n x_{ip} k=1n(xjpxkp)=i=1nxip
( 2 ) \displaystyle (2) (2)如果 x j \displaystyle x_j xj的第 p \displaystyle p p位是1,那么这一位的”&“运算和便是所有数在这一位的贡献,在考虑”|"运算,由于1或上任意数都是1,所以这一位将贡献n个1。即:
∑ i = 1 n ( x i p   &   x j p ) = ∑ i = 1 n x i p \displaystyle\sum_{i=1}^n (x_{ip}\, \& \, x_{jp})=\sum_{i=1}^n x_{ip} i=1n(xip&xjp)=i=1nxip
∑ k = 1 n ( x j p   ∣   x k p ) = n \displaystyle\sum_{k=1}^n (x_{jp} \, | \, x_{kp})=n k=1n(xjpxkp)=n
注意上述求和的结果是1的个数。

那么这篇题解就到此为止了,以下是我丑陋的代码

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
using namespace std;
#define ACCPECT return 0;
const int p = 1e9+7;
const int N = 5e5+10;
typedef long long ll;
typedef pair<ll,ll> PII;
int t;
ll n;
ll x[N];
ll cnt[61];
void solve(){
   scanf("%lld",&n);
   for(int i = 1; i <= 60; ++i) cnt[i] = 0;
   for(int i = 1; i <= n ; ++i)scanf("%lld",&x[i]);
   for(int j = 1; j <= n; ++j){//预处理每一位1的个数
        for(int i = 0; i < 60; ++i){
            if((x[j] >> (i*1ll)) & 1)++cnt[i+1];
        }
   }
   ll ans = 0;
   ll sum_and = 0, sum_or = 0;
   for(int i = 1; i <= n; ++i){
       sum_and = 0, sum_or = 0;
       for(int j = 0; j < 60; ++j){
           if((x[i] >> (j*1ll)) & 1){
               sum_and = sum_and + ((1ll << j)%p) * cnt[j+1] % p;
               sum_and %=p;
               //cout<<j<<":::"<<sum_and<<'\n';
               sum_or = sum_or + ((1ll << j)%p) * n % p;
               sum_or %= p;
               //cout<<j<<":::"<<sum_or<<'\n';
           } else {
                sum_or += ((1ll << j)%p) * cnt[j+1] % p;
                sum_or %= p;
           }
       }
       ans = (ans + (sum_and * sum_or) % p) % p;
   }
   cout<<ans<<'\n';
}
signed main(){
#ifdef ACM_LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    t = 1;
    scanf("%d",&t);
    while(t--){
        solve();
    }


    fclose(stdin);
    fclose(stdout);
    //--------
    ACCPECT;
    //--------
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值