数论 - Binary Vector - 2020牛客暑期多校训练营(第六场)

数论 - Binary Vector - 2020牛客暑期多校训练营(第六场)

题意:

( 题 意 真 的 读 不 懂 ) (题意真的读不懂) ()

随 机 n 个 n 维 01 向 量 , 询 问 这 个 n 个 向 量 线 性 无 关 的 概 率 f n 。 随机n个n维01向量,询问这个n个向量线性无关的概率f_n。 nn01n线fn

输入:

T 组 测 试 数 据 , T组测试数据, T

每 组 包 括 一 个 正 整 数 n 。 每组包括一个正整数n。 n

输出:

输 出 正 整 数 概 率 f i ( 1 ≤ i ≤ n ) 的 异 或 值 , 即 f 1 ⨁ f 2 ⨁ . . . ⨁ f n 。 答 案 对 1 0 9 + 7 取 模 。 输出正整数概率f_i(1≤i≤n)的异或值,即f_1\bigoplus f_2\bigoplus...\bigoplus f_n。答案对10^9+7取模。 fi(1in)f1f2...fn109+7

示例1
输入

3
1
2
3

输出

500000004
194473671
861464136

说明

f ( 1 ) = 1 2   f ( 2 ) = 3 8   f ( 3 ) = 21 64 f(1)=\frac{1}{2}\\\ \\ f(2)=\frac{3}{8}\\ \ \\f(3)=\frac{21}{64} f(1)=21 f(2)=83 f(3)=6421

数据范围:

1 ≤ T ≤ 1000 , 1 ≤ n ≤ 2 × 1 0 7 1≤T≤1000,1≤n≤2×10^7 1T10001n2×107


分析:

直 接 看 样 例 猜 公 式 : 直接看样例猜公式:

f n = ∏ i = 1 n ( 2 i − 1 ) ∏ i = 1 n 2 i f_n=\frac{\prod_{i=1}^n(2^i-1)}{\prod_{i=1}^n2^i} fn=i=1n2ii=1n(2i1)

看 到 本 题 n 的 范 围 , 必 然 是 要 预 处 理 出 2 × 1 0 7 内 的 f n 。 看到本题n的范围,必然是要预处理出2×10^7内的f_n。 n2×107fn

预 处 理 时 , 若 每 次 都 按 照 公 式 , 用 快 速 幂 计 算 f n , 时 间 复 杂 度 为 O ( n l o g ( m o d ) ) , 其 中 m o d 是 模 数 , 为 1 0 9 + 7 。 预处理时,若每次都按照公式,用快速幂计算f_n,时间复杂度为O(nlog(mod)),其中mod是模数,为10^9+7。 fnO(nlog(mod))mod109+7

联 想 到 求 乘 法 逆 元 时 , 用 递 推 优 化 到 O ( n ) , 因 此 , 本 题 尝 试 递 推 出 f n 与 f n − 1 的 关 系 。 联想到求乘法逆元时,用递推优化到O(n),因此,本题尝试递推出f_n与f_{n-1}的关系。 O(n)fnfn1

得 到 得到
f n = 2 n − 1 2 n f n − 1 = f n − 1 − f n − 1 2 n f_n=\frac{2^n-1}{2^n}f_{n-1}=f_{n-1}-\frac{f_{n-1}}{2^n} fn=2n2n1fn1=fn12nfn1

则 : 则:

f n % m o d = f n − 1 % m o d − f n − 1 × ( 2 n ) − 1 % m o d , 其 中 ( 2 n ) − 1 为 2 n 对 m o d 取 模 的 乘 法 逆 元 。 f_n\%mod=f_{n-1}\%mod-f_{n-1}×(2^n)^{-1}\%mod,其中(2^n)^{-1}为2^n对mod取模的乘法逆元。 fn%mod=fn1%modfn1×(2n)1%mod(2n)12nmod

此 时 我 们 发 现 , 若 要 每 次 按 照 快 速 幂 来 求 2 n 的 乘 法 逆 元 , 时 间 复 杂 度 仍 然 是 O ( n l o g ( m o d ) ) , 此时我们发现,若要每次按照快速幂来求2^n的乘法逆元,时间复杂度仍然是O(nlog(mod)), 2nO(nlog(mod))

因 此 , 我 们 再 考 虑 通 过 递 推 的 方 式 来 求 2 n 的 逆 元 。 因此,我们再考虑通过递推的方式来求2^n的逆元。 2n

得 到 : 得到:

( 2 n ) − 1 % m o d = ( 2 n − 1 ) − 1 % m o d × ( 2 ) − 1 % m o d (2^n)^{-1}\%mod=(2^{n-1})^{-1}\%mod×(2)^{-1}\%mod (2n)1%mod=(2n1)1%mod×(2)1%mod

这 样 , 我 们 又 能 够 在 O ( n ) 的 时 间 复 杂 度 内 预 处 理 出 2 i ( 1 ≤ i ≤ n ) 的 乘 法 逆 元 。 这样,我们又能够在O(n)的时间复杂度内预处理出2^i(1≤i≤n)的乘法逆元。 O(n)2i(1in)

最 后 我 们 预 处 理 出 前 缀 异 或 和 数 组 : S n = f 1 ⨁ f 2 ⨁ . . . ⨁ f n 即 可 。 最后我们预处理出前缀异或和数组:S_n=f_1\bigoplus f_2\bigoplus...\bigoplus f_n即可。 Sn=f1f2...fn

代码:

#include
#include
#include
#include
#include

#define ll long long

using namespace std;

const int N=2e7+10, mod=1e9+7;

int f[N],S[N];
int Pow_2[N];

int quick_pow(int a,int b,int mod)
{
    int res=1;
    while(b)
    {
        if(b&1) res=(ll)res*a%mod;
        a=(ll)a*a%mod;
        b>>=1;
    }
    return res;
}

void Init()
{
    Pow_2[1]=quick_pow(2,mod-2,mod);
    for(int i=2;i<=2e7;i++) Pow_2[i]=(ll)Pow_2[i-1]*Pow_2[1]%mod;
    
    f[1]=quick_pow(2,mod-2,mod);
    for(int i=2;i<=2e7;i++) f[i]=(f[i-1]-((ll)f[i-1]*Pow_2[i])%mod+mod)%mod;
    for(int i=1;i<=2e7;i++) S[i]=S[i-1]^f[i];
}

int main()
{  
    Init();
    int T,n;
    cin>>T;
    while(T--)
    {
        cin>>n;
        printf("%d\n",S[n]);
    }

    return 0;
}

你可能感兴趣的:(数论,算法,数论,乘法逆元,ACM)