题意:
( 题 意 真 的 读 不 懂 ) (题意真的读不懂) (题意真的读不懂)
随 机 n 个 n 维 01 向 量 , 询 问 这 个 n 个 向 量 线 性 无 关 的 概 率 f n 。 随机n个n维01向量,询问这个n个向量线性无关的概率f_n。 随机n个n维01向量,询问这个n个向量线性无关的概率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(1≤i≤n)的异或值,即f1⨁f2⨁...⨁fn。答案对109+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 1≤T≤1000,1≤n≤2×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=1n2i∏i=1n(2i−1)
看 到 本 题 n 的 范 围 , 必 然 是 要 预 处 理 出 2 × 1 0 7 内 的 f n 。 看到本题n的范围,必然是要预处理出2×10^7内的f_n。 看到本题n的范围,必然是要预处理出2×107内的fn。
预 处 理 时 , 若 每 次 都 按 照 公 式 , 用 快 速 幂 计 算 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。 预处理时,若每次都按照公式,用快速幂计算fn,时间复杂度为O(nlog(mod)),其中mod是模数,为109+7。
联 想 到 求 乘 法 逆 元 时 , 用 递 推 优 化 到 O ( n ) , 因 此 , 本 题 尝 试 递 推 出 f n 与 f n − 1 的 关 系 。 联想到求乘法逆元时,用递推优化到O(n),因此,本题尝试递推出f_n与f_{n-1}的关系。 联想到求乘法逆元时,用递推优化到O(n),因此,本题尝试递推出fn与fn−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} fn=2n2n−1fn−1=fn−1−2nfn−1
则 : 则: 则:
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=fn−1%mod−fn−1×(2n)−1%mod,其中(2n)−1为2n对mod取模的乘法逆元。
此 时 我 们 发 现 , 若 要 每 次 按 照 快 速 幂 来 求 2 n 的 乘 法 逆 元 , 时 间 复 杂 度 仍 然 是 O ( n l o g ( m o d ) ) , 此时我们发现,若要每次按照快速幂来求2^n的乘法逆元,时间复杂度仍然是O(nlog(mod)), 此时我们发现,若要每次按照快速幂来求2n的乘法逆元,时间复杂度仍然是O(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=(2n−1)−1%mod×(2)−1%mod
这 样 , 我 们 又 能 够 在 O ( n ) 的 时 间 复 杂 度 内 预 处 理 出 2 i ( 1 ≤ i ≤ n ) 的 乘 法 逆 元 。 这样,我们又能够在O(n)的时间复杂度内预处理出2^i(1≤i≤n)的乘法逆元。 这样,我们又能够在O(n)的时间复杂度内预处理出2i(1≤i≤n)的乘法逆元。
最 后 我 们 预 处 理 出 前 缀 异 或 和 数 组 : S n = f 1 ⨁ f 2 ⨁ . . . ⨁ f n 即 可 。 最后我们预处理出前缀异或和数组:S_n=f_1\bigoplus f_2\bigoplus...\bigoplus f_n即可。 最后我们预处理出前缀异或和数组:Sn=f1⨁f2⨁...⨁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;
}