(zay)讲的题
luogu CF1352G Special Permutation
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;
}