#luogu整理 P6599 异或

(zay)讲的题

luogu P6599 异或

luogu CF1352G Special Permutation

luogu CF1360F Spy-string

luogu P6599 异或

题目描述

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

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

且满足(forall iin[1,l]),都有 (a_iin[1,n]) 求:

[sum_{i=1}^lsum_{j=1}^{i-1}a_ioplus 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}^lsum_{j=1}^{i-1}a_ioplus 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 imes x imes (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
");
            continue;
        }
        long long f = 1ll << 40;
        while(f){
            f >>= 1;
            if(f > n) continue;
            ans += f * (k) * (l - (k));
            ans %= MOD;
        }
        printf("%lld
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Cao-Yucong/p/13125271.html