题目大意

  有一个 \(n\times k\) 的 01矩阵 \(C\),求有多少个 \(n\times m\) 的矩阵 \(A\) 和 \(m\times k\) 的矩阵 \(B\),满足 \(A\times B=C\)。系数对 \(2\) 取模。

  还有 \(q\) 次操作,每次会修改 \(C\) 中一行的值。

  要对每次修改后的矩阵计算答案。

  \(n,m,k,q\leq 1000\)。

题解

  可以发现,答案只跟 \(C\) 的秩有关。因为如果我们对 \(C\) 做行变换或列变换,那么就可以对 \(A\) 或 \(B\) 做同样的行变换或列变换,使得等式依然成立。

  记 \(C\) 的秩为 \(r\)。

  记 \(C_i\) 表示 \(C\) 的列向量,\(A_i\) 表示 \(A\) 的列向量。

  我们先枚举矩阵 \(A\),对于每个 \(C_i\),它都是由若干个 \(A_j\) 异或得到的,系数为 \(B_{j,i}\)。

  只有所有 \(C_i\) 都在 \(A_j\) 生成的线性空间中时,才有合法的 \(B\)。

  若 \(A\) 的秩为 \(x\),那么 \(B\) 方案数就有 \(2^{k(m-x)}\) 种。

  我们先对所有秩为 \(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;
}

【UOJ453】【集训队作业2018】围绕着我们的圆环 线性基 DP的更多相关文章

  1. UOJ #449. 【集训队作业2018】喂鸽子

    UOJ #449. [集训队作业2018]喂鸽子 小Z是养鸽子的人.一天,小Z给鸽子们喂玉米吃.一共有n只鸽子,小Z每秒会等概率选择一只鸽子并给他一粒玉米.一只鸽子饱了当且仅当它吃了的玉米粒数量\(≥ ...

  2. [UOJ422][集训队作业2018]小Z的礼物——轮廓线DP+min-max容斥

    题目链接: [集训队作业2018]小Z的礼物 题目要求的就是最后一个喜欢的物品的期望得到时间. 根据$min-max$容斥可以知道$E(max(S))=\sum\limits_{T\subseteq ...

  3. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)

    [UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...

  4. 【UOJ#422】【集训队作业2018】小Z的礼物(min-max容斥,轮廓线dp)

    [UOJ#422][集训队作业2018]小Z的礼物(min-max容斥,轮廓线dp) 题面 UOJ 题解 毒瘤xzy,怎么能搬这种题当做WC模拟题QwQ 一开始开错题了,根本就不会做. 后来发现是每次 ...

  5. UOJ#418. 【集训队作业2018】三角形

    #418. [集训队作业2018]三角形 和三角形没有关系 只要知道儿子放置的顺序,就可以直接模拟了 记录历史最大值 用一个pair(a,b):之后加上a个,期间最大值为增加b个 合并? A1+A2= ...

  6. 2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物

    T1: [集训队作业2018]小Z的礼物 我们发现我们要求的是覆盖所有集合里的元素的期望时间. 设\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\) ...

  7. [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP

    题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...

  8. UOJ#422. 【集训队作业2018】小Z的礼物

    #422. [集训队作业2018]小Z的礼物 min-max容斥 转化为每个集合最早被染色的期望时间 如果有x个选择可以染色,那么期望时间就是((n-1)*m+(m-1)*n))/x 但是x会变,中途 ...

  9. UOJ#428. 【集训队作业2018】普通的计数题

    #428. [集训队作业2018]普通的计数题 模型转化好题 所以变成统计有标号合法的树的个数. 合法限制: 1.根标号比子树都大 2.如果儿子全是叶子,数量B中有 3.如果存在一个儿子不是叶子,数量 ...

随机推荐

  1. 每周分享五个 PyCharm 使用技巧(一)

    PyCharm 是大多数 Python 开发者的首选 IDE,每天我们都在上面敲着熟悉的代码,写出一个又一个奇妙的功能. 一个每天都在使用的工具,如果能掌握一些高效的使用技巧,肯定会给我们的开发效率带 ...

  2. Recording and playing back data

    Recording and playing back data This tutorial will teach you how to record data from a running ROS s ...

  3. 关于C# 中的布尔运算符 "&" "|” 与 其类似的条件布尔运算符 "&&" "||" 区别说明。

    运算符使用说明如下:  分隔符 ———————————————————————————— 分隔符 ———————————————————————————— 上述两个运算符的结果与&和 | 完全 ...

  4. PHP 二维数组排序保持键名不变

    对二维数组指定的键名排序,首先大家想到的是array_multisort函数,关于array_multisort的用法我之前也写了一篇废话不多言,我们看个实例: <?php $data = ar ...

  5. flex 圣杯布局

    基本思路 圣杯布局分为3段:上.中.下.  中段被分为:左.中.右3块. 1:采用flex布局时,先把弹性容器主轴设置为垂直方向(flex-direction:column) 2:上.中.下3块弹性项 ...

  6. flex 增长与收缩

    flex:auto  将增长值与收缩值设置为1,基本大小为 auto . flex:none. 将增长值与收缩值设置为0,基本大小为 auto .也就是固定大小. 增长: 基本大小 + 额外空间 *( ...

  7. Android远程桌面助手之功能简介

    外国友人录制的ARDC的使用简介,非常不错,介绍得很详尽.

  8. java或Jmeter实现两个日期相加减(2003-06-01-2003-05-01)

    在beanshell中写入如下代码, import java.io.FileInputStream; SimpleDateFormat myFormatter = new SimpleDateForm ...

  9. Python3 isdigit()方法

    描述 Python isdigit() 方法检测字符串是否只由数字组成. 语法 isdigit()方法语法: str.isdigit() 参数 无. 返回值 如果字符串只包含数字则返回 True 否则 ...

  10. 【shell实例】定时21:00-21:05,循环调用DSQL脚本,其它时段自动退出

    1.功能描述: 每日21:00定时调起test.sh,循环调起DSQL脚本test.dsql,直到21:05程序自动退出,捕获日志到相应log文件中. 2.日志文件: (1)日期.log文件中含Err ...