专栏文章
题解:B4372 [GXPC-S 2025] 异或之力 / xor
B4372题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miohwoov
- 此快照首次捕获于
- 2025/12/02 19:29 3 个月前
- 此快照最后确认于
- 2025/12/02 19:29 3 个月前
题解 B4372 [GXPC-S 2025] 异或之力 / xor
题目要求:
对于给定的,我们考虑所有长度为的字符串(即数字到)。对于每个数字(当时),我们需要计算其异或之力,即所有分解(,为正整数)中,异或的最大值与最小值的差()。然后,我们要找出所有中最大的异或之力,并对取模。
关键结论:
1. 最小值Q:
- 当为偶数时,可以分解为, ,此时异或,所以最小值。
- 当为奇数时,设为的最低位的连续1的个数(从最低位开始连续的的个数),例如(二进制),最低位连续两个,则,此时最小值(即3)。实际上,我们可以取, ,则异或。而且这是最小的异或值。
2. 最大值P:
当为奇数时,最大值等于。因为可以分解为,,则异或(因为是偶数,最低位为,而的最低位为1,所以异或后最低位为1,而高位与相同,而的高位与相同,所以整个就是)。
当为偶数时,情况较复杂:
- Plan a. 如果是的幂(即),那么最大值。因为分解时,和不能同时为(因为那样异或为,不是最大),最大异或值可以通过分解为,(注意)得到,此时异或(因为的二进制是位全后面一个,而是后面个,异或后得到位中最高位为,其余位为,即实际上,我们计算:例如,,分解为和:的二进制,的二进制,异或得到即,而,不对,再分解为和:的二进制,的二进制,异或为,即,而。所以确实为。
Plan b. 如果是的倍数(即 )且不是的幂,那么最大值。例如,分解为和:4(0100)和8(1000)异或得到12(1100)?不对,应该是1100。但实际上,分解为6和6:异或为0;分解为5和7:5(0101)和7(0111)异或为2(0010);分解为4和8:4(0100)和8(1000)异或为1100(12),所以最大值是12。所以这里之前的结论可能有误。
重新审视最大值P(对于偶数C):
事实上,对于任意C,我们总是可以取A=1, B=C-1,则A异或B = 1^(C-1)。
当C为偶数时,C-1为奇数,且C-1的二进制表示就是C的二进制表示最低位0变为1,其他位不变。
那么1^(C-1)的结果:因为1只在最低位有1,而C-1的最低位是1,所以异或后最低位为0,而次低位?
实际上,1的二进制是...0001,C-1的二进制是...????1,异或后相当于将C-1的最低位置0,然后加上1(因为异或后最低位0,而原本最低位是1,相当于减1再置最低位为0?)这并不直接等于C。
实际上,我们有一个 重要性质:
对于任意,有,则
& 。
因此,的最大值就是C减去两倍的某个值的最小值,即当A和C-A的二进制位没有重叠的1时,A⊕B=C。
而什么时候没有重叠?
当A和C-A的二进制位没有同时为1的位,即A&C=0。
所以,最大值P等于满足A&C=0(即A是C的二进制表示中1对应的子集)的A中,A⊕(C-A)的最大值?
实际上,A⊕(C-A) = A+(C-A)(因为A和C-A没有公共1,所以异或等于加和),即C。
但是,只有当存在这样的分解时,最大值P才等于C。
然而,对于偶数C,如果C的二进制表示中最低位是0,那么A和B必须同时是奇数或偶数,但要求A和C-A没有公共1,这要求C的每一位至多在一个数中出现,即C的二进制位没有连续的1?
实际上,当C是2的幂时,我们无法将C分解成两个没有公共1的数(因为C只有一个1,那么A必须包含这个1,而B=0,但B不能为0,所以不行)。
因此,对于2的幂,我们无法达到C。
经过查阅和思考,我们有以下结论:
- 最大值P:我们可以通过分解A=(C-1), B=1,得到异或值为(C-1)^1。对于偶数C,C-1是奇数,1是奇数,但这样得到的值并不一定是最大的。
实际上,最大值P等于小于C的最大的2^k-1形式的数?这个结论也不对。
根据题目中的例子:C=6(110)时,分解成4和2,异或为4^2=6(因为4是100,2是010,异或110=6),分解成3和3,异或0。所以最大值是6,而6=6,所以对于6,P=6。但6不是2的幂,也不是4的倍数?6是2的倍数但不是4的倍数。
重新参考已知结论:
- 最小值Q:对于偶数C,Q=0;对于奇数C,Q=2^k-1(k是C的最低位连续1的个数)。
- 最大值P:对于任意C,最大值P等于C(当C不是2的幂时),当C是2的幂时,最大值P=C-2(因为无法取到C,最大只能取到C-2)。这个结论在例子C=8(2的幂)时,最大值为6(即8-2),而C=6(不是2的幂)时,最大值为6(即C本身)。
所以,我们可以得到:
{
当C为偶数且不是2的幂:
当C为偶数且是2的幂:
当C为奇数:
}
那么,我们要找最大的F(C)。显然:
- 对于偶数且不是2的幂:F(C)=C,最大值是2^n-1(当n>=2时,2^n-1是奇数,所以不考虑)
实际上,长度为n的字符串表示的最大数是2^n-1(奇数),所以偶数C的最大值是2^n-2(即全1串减去1,二进制为n-1个1后面一个0)。
而2^n-2是偶数,且当n>=3时,2^n-2不是2的幂(因为2的幂是1,2,4,8,...,而2^n-2显然不是,除非n=2,但n=2时,C最大为3(奇数)和2(偶数),但2是2的幂(2=2^1)
所以当n=2时,C=2是2的幂,F(2)=2-2=0;而C=3(奇数)时,k=2(因为3的二进制11,最低位连续两个1),所以F(3)=3-(2^2-1)=3-3=0。因此n=2时最大异或之力为0。
- 对于偶数且是2的幂:,而2的幂的最大值是2^(n-1)(因为长度为n,最大数2^n-1,而2的幂在[0,2^n-1]范围内的最大是2^(n-1)(因为2^n已经超出范围)
不对,2^(n-1)是2的幂,且小于2^n。但2^(n-1)的F(C)=2^(n-1)-2,而偶数非2的幂的最大值可以是2^n-2(当n>=3时,2^n-2不是2的幂),而2^n-2 > 2^(n-1)-2(当n>=3时),所以这种情况不如非2的幂的偶数。
对于奇数:。由于k至少为1,所以。而偶数非2的幂的,所以奇数的值一定小于偶数的最大值(因为偶数最大值可以达到,而奇数最大为,但,其中是的最低位连续1的个数实际上,的二进制是n个1,所以最低位连续1的个数,所以。
而其他奇数,比如(二进制n-2个1后面是01),那么(因为最低位是1,但前面是0?)所以。而(当n>=3时),所以奇数中最大的异或之力也不会超过
因此,最大的异或之力一定出现在偶数且非2的幂中,且最大值为(当时)。
那么,我们只需要验证时,其异或之力是否等于
验证:,其二进制是n-1个1后面一个0(例如时,,二进制110)。
-
最小值:因为是偶数,所以。
-
最大值:由于不是的幂(因为2的幂的二进制只有一个1,而有多个1),所以。
-
因此,。
那么,问题转化为:最大异或之力就是。
但是,我们还要考虑其他偶数非的幂的,比如,等,它们,而,所以最大值就是。
因此,答案就是(当时)。当时,我们已知答案为。
但是,注意题目要求:字符串可含前导零,即长度为的字符串表示的数范围是。而在时都是合法的。
所以,对于给定的:
若,输出;
-若,输出 。
然而,我们需要验证一下的情况:
长度为的字符串,(不行,因为是,而是)实际上,题目中例子,输出。
按照我们的公式:,符合。
但是,我们再看一个例子:,那么最大异或之力为。我们验证一下(二进制):
最小值:(因为偶数)
最大值:(因为不是的幂,所以最大值就是)
所以。
那么,有没有其他的大于?
没有,因为其他小于等于,而(奇数)的,就是最大值。
所以,结论成立。
因此,算法如下:
1. 读入。
2. 如果,输出。
3. 否则,计算。由于最大,所以使用快速幂。
注意:当时题目数据范围,所以不考虑。
代码实现:
CPP #include <iostream>
using namespace std;
const long long MOD = 1000000007;
long long fast_pow(long long base, long long exp, long long mod) {
long long res = 1;
base %= mod;
while (exp) {
if (exp & 1) res = (res * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}
return res;
}
int main() {
long long n;
cin >> n;
if (n == 2) {
cout << 0 << endl;
} else {
long long ans = (fast_pow(2, n, MOD) - 2;
if (ans < 0) ans += MOD; // 确保非负
cout << ans % MOD << endl;
}
return 0;
}
但是,我们注意:当时,,但是我们要求时输出。
所以上面的代码在时直接输出,其他情况输出(模意义下)。
然而,我们也可以统一处理:因为时,,但题目要求是,所以不能直接输出。因此,我们单独处理。
另一种思路:题目要求最大异或之力,而时,最大异或之力为(因为所有中,时,时),所以答案。
所以,按照上述代码即可。
但是,我们再看一下题目中时输出,我们的计算,正确。
因此,我们写代码。
注意:快速幂取模,因为可能很大,所以用快速幂模。
但是, 减去,然后取模,注意负数:如果 小于,那么减可能为负,但时,,所以不会出现负数(因为模,)。所以直接减再取模也可以。
然而,为了保险,我们这样写:
```cpp
CPPlong long ans = (fast_pow(2, n, MOD) - 2 + MOD) % MOD;
但时, ,所以减后,所以不会出现负数。
但是模意义下,如果 小于,那么减就会变成负数。
然而,当时, 至少是(模之前),但模之后可能小于吗?
不可能,因为模,而时,,模一个大于的数()还是,所以不会小于。所以直接减也可以。
但是,当很大时, 可能是一个到之间的数,而可能大于,所以模之后可能小于 实际上,时,,模()后,如果大于等于,那么减没问题;
如果模之后小于?但时,最小为,所以模之后最小为(如果整除,则模后为),但,而不可能被整除(因为是奇数,且大于),所以模之后不会为,且最小为?
不对,比如时,,而,所以 ,大于。
所以不会出现模后小于的情况(因为在时至少为,模后最小可能为?但是偶数,是奇数,所以模后不可能为?因为偶数模奇数,结果可能是到之间的任意整数,但是偶数,模奇数后还是偶数?不一定,比如模(偶数),所以模后可能是偶数也可能是奇数?不对,模运算不改变奇偶性:因为是奇数,设,则,因为是奇数,的奇偶性与相同,而是偶数,所以必须是偶数。
所以模后是偶数,且。而时,,所以要么大于等于,要么为。
但为时, ,那么,而,所以如果,那么,所以。
但是,当时,,而,要整除,而是素数(是素数)?不对,是素数吗?
实际上,是一个素数。所以整除是不可能的(因为只有素因子,而是奇数且大于的素数)。
所以不可能为。
同理,时,由于是偶数,且,所以是可能的。
但是,当时, ,那么,输出?
但,,模后等于,说明,那么,所以是的倍数,那么答案?
不对,因为我们的结论是答案应该是,而模后就是。这符合模运算。
因此,我们的计算应该是:
ans = (2^n - 2) % MOD
而(2^n - 2)mod MOD 可以写成 ( (2^n mod MOD) - 2 ) % MOD,但这样可能是负数,所以我们加上MOD再取模:
ans = ( (2^n mod MOD - 2) + MOD ) % MOD;
这样,当时,就是(2^n % MOD - 2);当(2^n % MOD) <2时(即等于0或1),那么加上MOD再减2,然后模MOD。
但是,我们之前已经说明,模后r是偶数,且r>=0,且r不等于0(因为2^n不能被MOD整除),所以r>=2?不一定,比如r=0不可能,但r=2,4,...。但r=0不可能,那么r最小可能是2吗?可能,比如当2^n mod MOD=2时,那么(2^n-2) mod MOD=0。所以我们需要这样处理。
所以,代码中:
long long a = fast_pow(2, n, MOD);
long long ans = (a - 2) % MOD;
if (ans < 0) ans += MOD;
// 或者直接:ans = (a - 2 + MOD) % MOD;
但考虑到a>=0,a-2可能为负(当a=0或1时),但a=0或1的情况在n>=3时不会出现?因为2^n>=8,模MOD后最小为2?不对,模后可能为0(但我们已经排除0,因为MOD不整除2^n)?不对,模后为0的情况不存在(因为MOD是素数且大于2,所以2^n mod MOD=0当且仅当2^n是MOD的倍数,而2^n的因子只有2,MOD是大于2的素数,所以不可能)。所以模后最小为2?也不一定,比如2^3=8,模7等于1(但7不是MOD,MOD是10^9+7)。实际上,模后可能是任意偶数(包括小于2的?不可能,因为模后最小为0,但0不存在,所以最小是2?不一定,比如模后可能是4,6,...,但2^n mod MOD=2的情况:例如,2^n=2 (mod MOD) 当且仅当2^n-2是MOD的倍数。所以我们需要考虑。
因此,为了安全,我们使用:
ans = (a - 2 + MOD) % MOD;
这样,如果a-2是负数,加上MOD就变成正数,然后模MOD就是正确结果。
所以,最终代码:
if (n == 2) {
cout << 0 << endl;
} else {
long long a = fast_pow(2, n, MOD);
long long ans = (a - 2 + MOD) % MOD;
cout << ans << endl;
}
但是,我们考虑n=3:a=8 mod (10^9+7)=8,ans=(8-2+10^9+7)%(10^9+7)=6,正确。
所以,我们这样写。
注意:当n很大时,快速幂能够高效计算2^n mod MOD。
因此,代码实现如下:
CPP
#include <iostream>
using namespace std;
const long long MOD = 1000000007;
long long fast_pow(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1)
result = (result * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}
return result;
}
int main() {
long long n;
cin >> n;
if (n == 2) {
cout << 0 << endl;
return 0;
}
long long a = fast_pow(2, n, MOD);
long long ans = (a - 2 + MOD) % MOD;
cout << ans << endl;
return 0;
}```
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...