填坑填坑..
感谢wwt耐心讲解啊..
如果要看这篇题解建议从上往下读不要跳哦..
30pts
把$A$和$C$看成$n$个$n$维向量,那$A_i$是否加入到$C_j$中就可以用$B_{i,j}$表示了
枚举矩阵$A$,求出它的秩$r$,如果$C$在$A$的线性空间内则$C$可以被$A$表示出来
那么$B$矩阵的方案数就是$(2^{n-r})^n$
这时候我们可以发现,由于枚举$A$覆盖了所有情况,秩相同的$C$的答案都是一样的
然后就可以打表算答案了..
60pts
如果不想看可以跳过这段
考虑用dp来代替上面枚举
定义$f_{i,j,k}$表示已经算了前$i$列,其中$C$有$j$个基在$A$的线性空间里,$A$有$k$个基在$C$的线性空间外的方案数
那么答案就是$\sum\limits_{k=1}^{n-r}f[n][r][k](2^{n-r-k})^n$
然后考虑第$i+1$列的情况
1)$j$、$k$都不变,即新的一列在$j+k$个基里面,方案为$2^{j+k}$
2)$j+1$、$k$不变,$k$不变的方案有$2^{r+k}$,再减去$j$不变的方案就是$2^{r+k}-2^{j+k}$
3)$j$不变,$k+1$,总方案有$2^n$,再减去$k$不变的方案就是$2^n-2^{r+k}$
这样就得到了一个$O(n^3)$的做法
100pts
30pts时说到,秩相同的$C$的答案都是一样的
那么我们不如把所有秩相同的$C$的总答案算起来再除以秩相同的个数
定义$f_{i,j}$表示已经算了前$i$列(也就是$n\times i$的矩阵),秩为$j$的方案数
那么总答案就是$\sum\limits_{x=r}^n f_{n,x}f_{x,r}(2^{n-x})^n$
再除以秩为$r$的方案数即可..
Code
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <bitset> #define LL long long using namespace std; const LL Maxn = 2010; const LL Mod = 1e9+7; bitset <Maxn> a[Maxn]; LL f[Maxn][Maxn]; LL pow2[Maxn], n; LL pow(LL x, LL k) { LL ret = 1; while(k){ if(k&1) ret = (ret*x)%Mod; x = (x*x)%Mod; k >>= 1; } return ret; } int main() { //freopen("mat.in", "r", stdin); //freopen("mat.out", "w", stdout); LL i, j, k; scanf("%lld", &n); pow2[0] = 1; for(i = 1; i <= n; i++) pow2[i] = (pow2[i-1] << 1) % Mod; f[0][0] = 1; for(i = 0; i < n; i++){ for(j = 0; j <= i; j++){ if(f[i][j] == 0) continue; f[i+1][j] = (f[i+1][j] + (f[i][j]*pow2[j])%Mod) % Mod; f[i+1][j+1] = (f[i+1][j+1] + (f[i][j]*((pow2[n]-pow2[j]+Mod)%Mod))%Mod) % Mod; } } for(i = 0; i < n; i++){ for(j = 0; j < n; j++){ LL x; scanf("%lld", &x); a[i][j] = x; } } LL r = 0; for(i = 0; i < n; i++){ for(j = r; j <= n; j++){ if(a[j][i] == 1){ swap(a[r], a[j]); break; } } if(a[r][i] == 0) continue; for(j = r+1; j < n; j++) if(a[j][i] == 1) a[j] ^= a[r]; r++; } LL ans = 0; for(i = r; i <= n; i++){ ans = (ans + ((f[n][i]*f[i][r])%Mod*pow(pow2[n], n-i))%Mod)%Mod; } ans = (ans*pow(f[n][r], Mod-2))%Mod; printf("%lld\n", ans); return 0; }