【UOJ453】【集训队作业2018】围绕着我们的圆环 线性基 DP
题目大意
有一个 \(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的更多相关文章
- UOJ #449. 【集训队作业2018】喂鸽子
UOJ #449. [集训队作业2018]喂鸽子 小Z是养鸽子的人.一天,小Z给鸽子们喂玉米吃.一共有n只鸽子,小Z每秒会等概率选择一只鸽子并给他一粒玉米.一只鸽子饱了当且仅当它吃了的玉米粒数量\(≥ ...
- [UOJ422][集训队作业2018]小Z的礼物——轮廓线DP+min-max容斥
题目链接: [集训队作业2018]小Z的礼物 题目要求的就是最后一个喜欢的物品的期望得到时间. 根据$min-max$容斥可以知道$E(max(S))=\sum\limits_{T\subseteq ...
- 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)
[UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...
- 【UOJ#422】【集训队作业2018】小Z的礼物(min-max容斥,轮廓线dp)
[UOJ#422][集训队作业2018]小Z的礼物(min-max容斥,轮廓线dp) 题面 UOJ 题解 毒瘤xzy,怎么能搬这种题当做WC模拟题QwQ 一开始开错题了,根本就不会做. 后来发现是每次 ...
- UOJ#418. 【集训队作业2018】三角形
#418. [集训队作业2018]三角形 和三角形没有关系 只要知道儿子放置的顺序,就可以直接模拟了 记录历史最大值 用一个pair(a,b):之后加上a个,期间最大值为增加b个 合并? A1+A2= ...
- 2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物
T1: [集训队作业2018]小Z的礼物 我们发现我们要求的是覆盖所有集合里的元素的期望时间. 设\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\) ...
- [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP
题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...
- UOJ#422. 【集训队作业2018】小Z的礼物
#422. [集训队作业2018]小Z的礼物 min-max容斥 转化为每个集合最早被染色的期望时间 如果有x个选择可以染色,那么期望时间就是((n-1)*m+(m-1)*n))/x 但是x会变,中途 ...
- UOJ#428. 【集训队作业2018】普通的计数题
#428. [集训队作业2018]普通的计数题 模型转化好题 所以变成统计有标号合法的树的个数. 合法限制: 1.根标号比子树都大 2.如果儿子全是叶子,数量B中有 3.如果存在一个儿子不是叶子,数量 ...
随机推荐
- [Nodejs] node的fs模块
fs 模块 Node.js 提供一组类似 UNIX(POSIX)标准的文件操作 API. Node 导入文件系统模块(fs).Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如 ...
- 校园生活app结对开发第一天
今天刚开始开发,要安装android studio及熟悉软件操作
- PC打开多个微信
打开记事本 在哪个盘就打开哪个盘 例如我的D盘 输入这一串 D:CD 微信路径start WeChat.exe&WeChat.exe 保存然后改后缀名为.bat 然后运行 运行前要关掉之前开的 ...
- 从零开始学安全(四十六)●sqli-labs 1-4关 涉及的知识点
Less-1 到Less-4 基础知识注入 我们可以在 http://127.0.0.1/sqllib/Less-1/?id=1 后面直接添加一个 ‘ ,来看一下效果: 从上述错误当中,我们可以看到 ...
- MySQL InnoDB 存储引擎探秘
在MySQL中InnoDB属于存储引擎层,并以插件的形式集成在数据库中.从MySQL5.5.8开始,InnoDB成为其默认的存储引擎.InnoDB存储引擎支持事务.其设计目标主要是面向OLTP的应用, ...
- web前端图片上传(2)
今天发现了一种ajax上传图片的方式,是以前没有用过的,首先来说下为什么要用这种方式.是因为原来后台是用的form表单的方式来提交表单数据的.但是觉得呢,这种方式不太好,因为要刷新页面,前台只用控制台 ...
- 微信web页面返回刷新
问题:在微信web页面开发的过程中,会遇到返回上一个页面数据没有刷新的情况. 解决方案:在该页面监控用户的浏览,每次加载都刷新页面. window.onpageshow = function(even ...
- 自定义编译gdal库
作者:朱金灿 来源:http://blog.csdn.net/clever101 使用下载下来的gdal库的makefile来编译gdal库,生成的gdal库的名字debug版本和release版本都 ...
- BestSync多终端文件资料同步利器
分享一款多终端文件同步的强力软件,windows下使用. 我这里的多终端意思是,多台电脑.移动存储.云端. 就我个人而言,实用性在于移动硬盘和电脑上都有的文件,比如保存项目资料,电脑上需要编辑,有时外 ...
- token登录
①定义 Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再 ...