#luogu整理 P6599 异或

\(zay\)讲的题

luogu P6599 异或

luogu CF1352G Special Permutation

luogu CF1360F Spy-string

luogu P6599 异或

题目描述

\(T\) 组询问,每次给定两个正整数 \(n,l\)

你需要构造一个长度为 \(l\) 的正整数序列 \(a\)(编号从 \(1\)\(l\)),

且满足\(\forall i\in[1,l]\),都有 \(a_i\in[1,n]\) 求:

\[\sum_{i=1}^l\sum_{j=1}^{i-1}a_i\oplus a_j \]

的最大值。

为了避免答案过大,对于每组询问,只需要输出这个最大值对 \(10^9+7\) 取模的结果。

样例

\(in\)

1
2 3

\(out\)

6

\(in2\)

2
114 514
1919 180

\(out2\)

8388223
16580700

题意

异或:两个数按位运算,相同返回0,不同返回1。

题中的

\[\sum_{i=1}^l\sum_{j=1}^{i-1}a_i\oplus a_j \]

大意就是这样:

int ans = 0;
for(i = 1 → l)
    for(j = 1 → i-1)
        ans += a[i] ^ a[j];

即选定一个数,让这个数分别和他后面的每一个数字做异或运算,把得到的值加起来。现在要我们构造这么一个数列,使得这样的结果最大。

思路

把每个数字转换成二进制,疯狂观察:

125 = 1 1 1 1 1 0 1
67  = 1 0 0 0 0 1 1 
89  = 1 0 1 1 0 0 1
101 = 1 1 0 0 1 0 1

我们可以发现,第k位对整个答案的贡献应该是这一位0的个数乘以这一位1的个数。

当然因为在计算机里面进行的是按位运算,最后别忘了乘以\(2^k\)。设0的个数是x,则贡献就是:

\[2^k \times x \times (l - x) \]

根据二次函数的知识我们可以知道,要想让这个东西取得最大值,x应当等于\(\frac l2\)。那么我们只需要对每一位构造\(\frac l2\)个1,\(x - \frac l2\)个0,就能够使得最终的答案最大。那么就没有必要知道每一个数到底有多大,只需要判断对高位在哪里就可以了。

\(Code\)

long long t,l,n;
long long ans = 0;
int main(){
    cin >> t;
    while(t--){
        ans = 0;
        cin >> n >> l;
        long long k = l >> 1;
        if(n == 1){
            printf("0\n");
            continue;
        }
        long long f = 1ll << 40;
        while(f){
            f >>= 1;
            if(f > n) continue;
            ans += f * (k) * (l - (k));
            ans %= MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2020-06-14 16:16  CYC的幸福生活  阅读(154)  评论(0编辑  收藏  举报