数论 - Binary Vector - 2020牛客暑期多校训练营(第六场)
题意:
(题意真的读不懂)
随机n个n维01向量,询问这个n个向量线性无关的概率fn。
输入:
T组测试数据,
每组包括一个正整数n。
输出:
输出正整数概率fi(1≤i≤n)的异或值,即f1⨁f2⨁...⨁fn。答案对109+7取模。
示例1
输入
3
1
2
3
输出
500000004
194473671
861464136
说明
f(1)=21 f(2)=83 f(3)=6421
数据范围:
1≤T≤1000,1≤n≤2×107
分析:
直接看样例猜公式:
fn=∏i=1n2i∏i=1n(2i−1)
看到本题n的范围,必然是要预处理出2×107内的fn。
预处理时,若每次都按照公式,用快速幂计算fn,时间复杂度为O(nlog(mod)),其中mod是模数,为109+7。
联想到求乘法逆元时,用递推优化到O(n),因此,本题尝试递推出fn与fn−1的关系。
得到
fn=2n2n−1fn−1=fn−1−2nfn−1
则:
fn%mod=fn−1%mod−fn−1×(2n)−1%mod,其中(2n)−1为2n对mod取模的乘法逆元。
此时我们发现,若要每次按照快速幂来求2n的乘法逆元,时间复杂度仍然是O(nlog(mod)),
因此,我们再考虑通过递推的方式来求2n的逆元。
得到:
(2n)−1%mod=(2n−1)−1%mod×(2)−1%mod
这样,我们又能够在O(n)的时间复杂度内预处理出2i(1≤i≤n)的乘法逆元。
最后我们预处理出前缀异或和数组:Sn=f1⨁f2⨁...⨁fn即可。
代码:
#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;
}