有一个 \(n\times k\) 的 01矩阵 \(C\),求有多少个 \(n\times m\) 的矩阵 \(A\) 和 \(m\times k\) 的矩阵 \(B\),知足 \(A\times B=C\)。系数对 \(2\) 取模。dom
还有 \(q\) 次操做,每次会修改 \(C\) 中一行的值。spa
要对每次修改后的矩阵计算答案。code
\(n,m,k,q\leq 1000\)。get
能够发现,答案只跟 \(C\) 的秩有关。由于若是咱们对 \(C\) 作行变换或列变换,那么就能够对 \(A\) 或 \(B\) 作一样的行变换或列变换,使得等式依然成立。string
记 \(C\) 的秩为 \(r\)。it
记 \(C_i\) 表示 \(C\) 的列向量,\(A_i\) 表示 \(A\) 的列向量。io
咱们先枚举矩阵 \(A\),对于每一个 \(C_i\),它都是由若干个 \(A_j\) 异或获得的,系数为 \(B_{j,i}\)。function
只有全部 \(C_i\) 都在 \(A_j\) 生成的线性空间中时,才有合法的 \(B\)。class
若 \(A\) 的秩为 \(x\),那么 \(B\) 方案数就有 \(2^{k(m-x)}\) 种。im
咱们先对全部秩为 \(r\) 的矩阵统计方案数,再除以秩为 \(r\) 的矩阵个数便可。
枚举 \(A\) 的秩 \(x\),那么合法的 \(C\) 的每一个列向量均可以由 \(A\) 的列向量组合而成,能够写成一个 \(k\times x\) 的矩阵,且这个矩阵的秩为 \(r\)。
记 \(f_{i,j}\) 表示 \(n\times i\) 的秩为 \(j\) 的矩阵个数,\(p_{i,j}\) 表示 \(k\times i\) 的秩为 \(j\) 的矩阵个数,那么对答案的贡献就是 \(f_{m,x}g_{x,r}2^{k(m-x)}\)。
最后把答案除以 \(f_{k,r}\) 便可。
先预处理出 \(f,g\),就能够在 \(O(n)\) 内回答一次询问。
如今咱们还要求 \(C\) 的秩 \(r\)。
对于线性基中的每一个向量和全部 \(0\) 向量维护这个向量是由哪些向量异或获得的。
在删除一个向量 \(x\) 时,找到一个包含 \(x\) 的 \(0\) 向量,若是没有就找线性基里位最低的包含 \(x\) 的向量,把这个向量的信息异或到其余包含 \(x\) 的向量的信息中便可。这样在删除时不会影响线性基中更高位的向量。
时间复杂度:\(O(\frac{(n+q)n^2}{w})\)
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<ctime> #include<functional> #include<cmath> #include<vector> #include<assert.h> #include<bitset> using namespace std; using std::min; using std::max; using std::swap; using std::sort; using std::reverse; using std::random_shuffle; using std::lower_bound; using std::upper_bound; using std::unique; using std::vector; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef std::pair<int,int> pii; typedef std::pair<ll,ll> pll; void open(const char *s){ #ifndef ONLINE_JUDGE char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } void open2(const char *s){ #ifdef DEBUG char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;} void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');} int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;} int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;} const ll p=1000000007; const int N=1010; ll fp(ll a,ll b) { ll s=1; for(;b;b>>=1,a=a*a%p) if(b&1) s=s*a%p; return s; } typedef bitset<N> orzzjt; typedef pair<orzzjt,orzzjt> zjtakioi2019; ll pw[N*N]; ll f[N][N],g[N][N]; void add(ll &a,ll b) { a=(a+b)%p; } int n,m,k; ll solve(int x) { ll res=0; for(int i=x;i<=n&&i<=m;i++) res=(res+f[m][i]*g[i][x]%p*pw[k*(m-i)])%p; res=res*fp(f[m][x],p-2)%p; res=(res%p+p)%p; return res; } ll e[N]; void init() { pw[0]=1; for(int i=1;i<=1000000;i++) pw[i]=pw[i-1]*2%p; f[0][0]=1; for(int i=0;i<1000;i++) for(int j=0;j<=i&&j<=n;j++) if(f[i][j]) { add(f[i+1][j],f[i][j]*pw[j]); add(f[i+1][j+1],f[i][j]*(pw[n]-pw[j])); } g[0][0]=1; for(int i=0;i<1000;i++) for(int j=0;j<=i&&j<=k;j++) if(g[i][j]) { add(g[i+1][j],g[i][j]*pw[j]); add(g[i+1][j+1],g[i][j]*(pw[k]-pw[j])); } for(int i=0;i<=n&&i<=k;i++) e[i]=solve(i); } zjtakioi2019 a[N]; orzzjt b[N]; int t; int r; void insert(orzzjt x,int v) { orzzjt y; y.set(v); for(int i=1000;i>=1;i--) if(x[i]) { if(!a[i].first.any()) { a[i].first=x; a[i].second=y; r++; return; } x^=a[i].first; y^=a[i].second; } t++; b[t]=y; } void erase(int x) { for(int i=1;i<=t;i++) if(b[i][x]) { swap(b[i],b[t]); t--; for(int j=1;j<=1000;j++) if(a[j].second[x]) a[j].second^=b[t+1]; for(int j=1;j<=t;j++) if(b[j][x]) b[j]^=b[t+1]; return; } for(int i=1;i<=1000;i++) if(a[i].second[x]) { for(int j=i+1;j<=1000;j++) if(a[j].second[x]) { a[j].first^=a[i].first; a[j].second^=a[i].second; } a[i].first=a[i].second=orzzjt(); r--; return; } } int main() { open("c"); int q,type; scanf("%d%d%d%d%d",&n,&m,&k,&q,&type); init(); int x,y; for(int i=1;i<=n;i++) { orzzjt s; for(int j=1;j<=k;j++) { scanf("%d",&x); if(x) s.set(j); } insert(s,i); } ll ans=e[r]; printf("%lld\n",ans); for(int i=1;i<=q;i++) { scanf("%d",&x); x^=type*ans; erase(x); orzzjt s; for(int j=1;j<=k;j++) { scanf("%d",&y); if(y) s.set(j); } insert(s,x); ans=e[r]; printf("%lld\n",ans); } return 0; }