题解:B4372 [GXPC-S 2025] 异或之力 / xor

· · 题解

题解 B4372 [GXPC-S 2025] 异或之力 / xor

题目要求:

对于给定的n,我们考虑所有长度为n01字符串(即数字02^n-1)。对于每个数字C(当C>1时),我们需要计算其异或之力,即所有分解C=A+BA,B为正整数)中,A异或B的最大值P与最小值Q的差(P-Q)。然后,我们要找出所有C中最大的异或之力,并对10^9+7取模。

关键结论:

1. 最小值Q:

当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+B=C,则

 因此,$A⊕B$的最大值就是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本身)。  所以,我们可以得到:   $ F(C) = P - Q =

{

当C为偶数且不是2的幂:P=C, Q=0 -> F(C)=C;   当C为偶数且是2的幂:P=C-2, Q=0 -> F(C)=C-2;   当C为奇数:P=C, Q=2^k-1 -> F(C)=C - (2^k-1);  }    那么,我们要找最大的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^(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的幂的偶数。

对于奇数:F(C)=C-(2^k-1)。由于k至少为1,所以F(C)<=C-1。而偶数非2的幂的F(C)=C,所以奇数的值一定小于偶数的最大值(因为偶数最大值可以达到2^n-2,而奇数最大为2^n-1,但F(2^n-1)= (2^n-1) - (2^k-1),其中k2^n-1的最低位连续1的个数实际上,2^n-1的二进制是n个1,所以最低位连续1的个数k=n,所以F(2^n-1)= (2^n-1) - (2^n-1)=0

而其他奇数,比如2^n-3(二进制n-2个1后面是01),那么k=1(因为最低位是1,但前面是0?)所以F(2^n-3)= (2^n-3) - (2^1-1)=2^n-4。而2^n-4 < 2^n-2(当n>=3时),所以奇数中最大的异或之力也不会超过2^n-2。

 因此,最大的异或之力一定出现在偶数且非2的幂中,且最大值为2^n-2(当C=2^n-2时)。   那么,我们只需要验证C=2^n-2时,其异或之力是否等于2^n-2。

 验证:C=2^n-2,其二进制是n-1个1后面一个0(例如n=3时,C=6,二进制110)。  

 那么,问题转化为:最大异或之力就是2^n-2

   但是,我们还要考虑其他偶数非2的幂的C,比如2^n-42^n-6等,它们F(C)=C,而C<2^n-2,所以最大值就是2^n-2。    因此,答案就是2^n-2(当n>=3时)。当n=2时,我们已知答案为0。  但是,注意题目要求:字符串可含前导零,即长度为n的字符串表示的数范围是[0, 2^n-1]。而C=2^n-2n>=2时都是合法的。    所以,对于给定的n

n=2,输出0

-若n>=3,输出(2^n-2) mod (10^9+7)

 然而,我们需要验证一下n=3的情况:   长度为3的字符串,C=6111不行,因为1117,而6110)实际上,题目中例子n=3,输出6

  按照我们的公式:2^3-2=6,符合。

但是,我们再看一个例子:n=4,那么最大异或之力为2^4-2=14。我们验证一下C=14(二进制1110):

最小值Q0(因为偶数)

最大值P14(因为14不是2的幂,所以最大值就是14

所以F(14)=14

那么,有没有其他CF(C)大于14

没有,因为其他C小于等于15,而15(奇数)的F(15)=15-(2^4-1)=014就是最大值。

 所以,结论成立。

 因此,算法如下:

  1. 读入n

  2. 如果n==2,输出0

  3. 否则,计算(2^n - 2) mod (10^9+7)。由于n最大10^9,所以使用快速幂。

 注意:当n=1时题目数据范围n>=2,所以不考虑。  代码实现:

    #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;
    }

 但是,我们注意:当n=2时,2^2-2=2,但是我们要求n=2时输出0

所以上面的代码在n=2时直接输出0,其他情况输出2^n-2(模意义下)。  然而,我们也可以统一处理:因为n=2时,2^2-2=2,但题目要求是0,所以不能直接输出2^n-2。因此,我们单独处理n=2

 另一种思路:题目要求最大异或之力,而n=2时,最大异或之力为0(因为所有C中,C=2F(2)=0C=3F(3)=0),所以答案0

 所以,按照上述代码即可。

 但是,我们再看一下题目中n=3时输出6,我们的计算2^3-2=6,正确。  因此,我们写代码。

 注意:快速幂取模,因为2^n可能很大,所以用快速幂模10^9+7。  但是,2^n mod MOD 减去2,然后取模,注意负数:如果2^n mod MOD 小于2,那么减2可能为负,但n>=3时,2^n>=8,所以不会出现负数(因为模10^9+78-2=6>0)。所以直接减再取模也可以。  然而,为了保险,我们这样写:

long long ans = (fast_pow(2, n, MOD) - 2 + MOD) % MOD;

 但n>=3时,2^n mod MOD>=8,所以减2>=6,所以不会出现负数。

但是模意义下,如果2^n mod MOD小于2,那么减2就会变成负数。

然而,当n>=3时,2^n mod MOD至少是8(模之前),但模之后可能小于2吗?

不可能,因为模10^9+7,而n>=3时,2^n>=8,模一个大于8的数(10^9+7)还是8,所以不会小于2。所以直接减也可以。

 但是,当n很大时,2^n mod MOD 可能是一个0MOD-1之间的数,而2^n可能大于MOD,所以模之后可能小于2 实际上,n>=3时,2^n>=8,模MOD10^9+7)后,如果大于等于8,那么减2没问题;

如果模之后小于8?但n>=3时,2^n最小为8,所以模之后最小为0(如果MOD整除2^n,则模后为0),但MOD=10^9+7,而2^n(n>=3)不可能被10^9+7整除(因为10^9+7是奇数,且大于8),所以模之后不会为0,且最小为8

不对,比如n=30时,2^30=1073741824,而MOD=1000000007,所以2^30 mod MOD=1073741824 % 1000000007 = 73741817,大于2

所以不会出现模后小于2的情况(因为2^nn>=3时至少为8,模MOD后最小可能为1?但2^n是偶数,MOD是奇数,所以模后不可能为1?因为偶数模奇数,结果可能是0MOD-1之间的任意整数,但2^n是偶数,模奇数后还是偶数?不一定,比如53=2(偶数),所以模后可能是偶数也可能是奇数?不对,模运算不改变奇偶性:因为MOD是奇数,设2^n = k*MOD + r,则r=2^n - k*MOD,因为MOD是奇数,k*MOD的奇偶性与k相同,而2^n是偶数,所以r必须是偶数。

所以模后r是偶数,且r>=0。而n>=3时,2^n>=8,所以r要么大于等于8,要么为0,2,4,6

r0,2,4,6时,2^n mod MOD=r,那么2^n = k*MOD + r,而2^n>=8,所以如果r<8,那么k*MOD=2^n-r>=8-6=2,所以k>=1

但是,当r=0时,2^n=k*MOD,而MOD=10^9+72^n要整除MOD,而MOD是素数(10^9+7是素数)?不对,10^9+7是素数吗?

实际上,10^9+7是一个素数。所以2^n整除10^9+7是不可能的(因为2^n只有素因子2,而10^9+7是奇数且大于2的素数)。

所以r不可能为0

同理,r=2,4,6时,由于r是偶数,且0<=r<MOD,所以是可能的。

但是,当r=2时,2^n mod MOD=2,那么2^n-2=0,输出0

n>=32^n>=8,模MOD后等于2,说明2^n = k*MOD+2,那么2^n-2=k*MOD,所以2^n-2MOD的倍数,那么答案0

不对,因为我们的结论是答案应该是2^n-2,而模MOD后就是0。这符合模运算。

 因此,我们的计算应该是:   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);当(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。  因此,代码实现如下:



#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;
}```