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

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

题意:

( ) (题意真的读不懂)

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

输入:

T T组测试数据,

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取模。

示例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}

数据范围:

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


分析:

直接看样例猜公式:

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}

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

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。

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

得到
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}

则:

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取模的乘法逆元。

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

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

得到:

( 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

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

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

代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>

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

猜你喜欢

转载自blog.csdn.net/njuptACMcxk/article/details/107622795
今日推荐