#luogu整理 P6599 异或
\(zay\)讲的题
luogu CF1352G Special Permutation
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;
}