[CSP-S模拟测试]:matrix(DP)
题目描述
求出满足以下条件的$n\times m$的$01$矩阵个数:
(1)第$i$行第$1~l_i$列恰好有$1$个$1$。
(2)第$i$行第$r_i~m$列恰好有$1$个$1$。
(3)每列至多有$1$个$1$。
输入格式
第一行两个整数$n,m$。接下来$n$行每行$2$个整数$l_i,r_i$。
输出格式
一行一个整数表示答案。对998244353取模。
样例
样例输入
2 6
2 4
5 6
样例输出
12
数据范围与提示
对于$20\%$的数据,$n,m\leqslant 12$。
对于$40\%$的数据,$n,m\leqslant 50$。
对于$70\%$的数据,$n,m\leqslant 300$。
对于$100\%$的数据,$n,m\leqslant 3000$,$1\leqslant l_i<r_i\leqslant m$。
题解
看到这道题,首先应该想到组合数,然后……
我一开始想的是容斥,正好能模过样例,但是忽然发现是一个多步容斥,无语……
$20\%$算法:
使劲搜就好了,别搜错了,记得别用clock(),递归函数里clock()返回值玄学(考场上被这东西干没了40分……)。
时间复杂度:$\Theta(m^n)~\Theta(m^{\frac{n^2}{4}})$。
期望得分:$20$分。
$100\%$算法:
考虑$DP$,定义有点意思,定义$dp[i][j]$表示当前到了第$i$列,已经有$j$行在右侧区间放$1$的方案数。
下面来解释一下,注意右侧区间不是第$i$列的右侧,而是$r_i$,理解这东西我用了半个小时……
下面在来看一下如何转移:
首先,要预处理一些东西,用一个$fl$数组表示在第$i$列以前结束的左区间的个数,$fr$数组表示在地$i$列以前开始的右区间的个数,注意这里的左区间和右区间也指的是$l_i$和$r_i$。
然后就来明确两个值:$i-j-l[i-1]$就是第$i$列左侧$1$的个数,$l[i]-l[i-1]$就是在当前列结束的左区间的个数。
那么,将有$4$种转移方式:
$\alpha.$如果$(i-j-l[i-1])<l[i]-l[i-1]$那么当前的$i$列$j$行以及往后更大的$j$都不可能贡献方案数,所以直接$break$即可。
$\beta.$$dp[i][j]=dp[i][j]\times A_{i-j-l[i-1]}^{l[i]-l[i-1]}$,需要用到这种转移是因为我们在下面两种转移的时候不方便执行这种操作。
$\gamma.$$dp[i-1][j]+=dp[i][j]$,这种就是不在这列放$1$,进行直接转移。
$\delta.$$dp[i][j]=dp[i-1][j-1]\times (r[i]-j+1)$这种就是放$1$,方案数为$r[i]-j+1$。
排列数$A$可以选择直接用$\Theta(n\times m)$进行打表即可。
时间复杂度:$\Theta(n\times m)$。
期望得分:$100$分。
代码时刻
$20\%$算法:
#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
int n,m;
struct rec{int l,r;}q[3001];
bool vis[3001];
int ans;
int lmax,rmin=10000;
int qpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void dfs(int x)
{
if(x>n)
{
ans=(ans+1)%mod;
return;
}
for(int i=1;i<=q[x].l;i++)
for(int j=q[x].r;j<=m;j++)
if(!vis[i]&&!vis[j])
{
vis[i]=vis[j]=1;
dfs(x+1);
vis[i]=vis[j]=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
lmax=max(lmax,q[i].l);
rmin=min(rmin,q[i].r);
}
dfs(1);
printf("%d",ans);
return 0;
}
$100\%$算法:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int l[5000],r[5000],A[5000][5000],dp[5000][5000];
void pre_work()
{
A[0][0]=1;
for(int i=1;i<=m;i++)
{
A[i][0]=1;
for(int j=1;j<=i;j++)
A[i][j]=(1LL*A[i][j-1]*(i-j+1))%998244353;
}
}
int main()
{
scanf("%d%d",&n,&m);
pre_work();
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
l[x]++;r[y]++;
}
for(int i=1;i<=m;i++)
{
l[i]+=l[i-1];
r[i]+=r[i-1];
}
dp[1][0]=1;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=r[i];j++)
{
if(i-j<l[i])break;
dp[i][j]=1LL*dp[i][j]*A[i-j-l[i-1]][l[i]-l[i-1]]%998244353;
dp[i+1][j]=(dp[i+1][j]+dp[i][j])%998244353;
dp[i+1][j+1]=(dp[i+1][j+1]+1LL*dp[i][j]*(r[i+1]-j)%998244353)%998244353;
}
}
cout<<dp[m][n];
return 0;
}
rp++
[CSP-S模拟测试]:matrix(DP)的更多相关文章
- noi2019模拟测试赛(四十七)
noi2019模拟测试赛(四十七) T1与运算(and) 题意: 给你一个序列\(a_i\),定义\(f_i=a_1\&a_2\&\cdots\&a_i\),求这个序列的所 ...
- [考试反思]1109csp-s模拟测试106:撞词
(撞哈希了用了模拟测试28的词,所以这次就叫撞词吧) 蓝色的0... 蓝色的0... 都该联赛了还能CE呢... 考试结束前15分钟左右,期望得分300 然后对拍发现T2伪了写了一个能拿90分的垃圾随 ...
- [考试反思]1003csp-s模拟测试58:沉淀
稳住阵脚. 还可以. 至少想拿到的分都拿到了,最后一题的确因为不会按秩合并和线段树分治而想不出来. 对拍了,暴力都拍了.挺稳的. 但是其实也有波折,险些被卡内存. 如果内存使用不连续或申请的内存全部使 ...
- [考试反思]0814NOIP模拟测试21
前两名是外校的240.220.kx和skyh拿到了190的[暴力打满]的好成绩. 我第5是170分,然而160分就是第19了. 在前一晚上刚刚爆炸完毕后,心态格外平稳. 想想前一天晚上的挣扎: 啊啊啊 ...
- csp-s模拟测试98
csp-s模拟测试98 $T1$??不是我吹我轻松手玩20*20.$T2$装鸭好像挺可做?$T3$性质数据挺多提示很明显? $One$ $Hour$ $Later$ 这$T1$什么傻逼题真$jb$难调 ...
- csp-s模拟测试97
csp-s模拟测试97 猿型毕露.水题一眼秒,火题切不动,还是太菜了. $T1$看了一会儿感觉$woc$期望题$T1??$假的吧??. $T2$秒. $T3$什么玩意儿. 40 01:24:46 00 ...
- csp-s模拟测试95
csp-s模拟测试95 去世场祭. $T1$:这不裸的除法分块吗. $T2$:这不裸的数据结构优化$Dp$吗. $T3$:这不裸的我什么都不会搜索骗$30$分吗. 几分钟后. 这除法分块太劲了..(你 ...
- csp-s模拟测试93
csp-s模拟测试93 自闭场. $T1$想到$CDQ$,因为复杂度少看见一个$0$打了半年还用了$sort$直接废掉,$T2$,$T3$直接自闭暴力分都没有.考场太慌了,心态不好. 02:07:34 ...
- csp-s模拟测试92
csp-s模拟测试92 关于$T1$:最短路这一定建边最短路. 关于$T2$:傻逼$Dp$这一定线段树优化$Dp$. 关于$T3$:最小生成树+树P+换跟一定是这样. 深入(?)思考$T1$:我是傻逼 ...
随机推荐
- 【官网】2019.5.19 CentOS8.0 最新进展
Contents CentOS 8 Rough Status Page General Steps Architectures Main architectures AltArch Current T ...
- 剑指Offer编程题(Java实现)——两个链表的第一个公共结点
题目描述: 输入两个链表,找出它们的第一个公共结点. 思路一: 设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a. ...
- KMP字符串匹配 模板 洛谷 P3375
KMP字符串匹配 模板 洛谷 P3375 题意 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.(如果 ...
- NOIP提高组初赛难题总结
NOIP提高组初赛难题总结 注:笔者开始写本文章时noip初赛新题型还未公布,故会含有一些比较老的内容,敬请谅解. 约定: 若无特殊说明,本文中未知数均为整数 [表达式] 表示:在表达式成立时它的值为 ...
- 查看linux中所有用户的三种方式
通过使用/etc/passwd 文件,getent命令,compgen命令这三种方法查看系统中用户的信息. Linux 系统中用户信息存放在/etc/passwd文件中. 这是一个包含每个用户基本信息 ...
- 前端开发-css基础入门
CSS cascading(层叠) style(样式) sheet(表) css注释 /* 注释内容 */ 快捷键:ctrl ? 引入方式 <!-- 1.行间样式 --> <div ...
- java创建对象的5种方法
java是面向对象的,所以在使用中经常会去创建对象,而我们一般创建对象只会使用new关键字去创建,这里给大家总结一下在java中创建对象的5中方法: 使用new关键字 } → 调用了构造函数 使用Cl ...
- R语言模型选择之精度准则与最大值法问题
在模型选择中我们一般用caret包train函数建立模型,并对模型进行评判 方法1: ) tr_control<-trainControl(method = ) # 创建随机森林模型 model ...
- 继续死磕python
一.数据运算 算术运算 比较运算 赋值运算 逻辑运算 成员运算 身份运算 位运算 其中左右移运算是逻辑左右移即缺失位补0,而算数右移缺失补符号位(注意逻辑运算都是补码运算即都取补码再运算,然后结果也是 ...
- Linux学习--第五天--vim使用、rpm命令
vim使用 三种模式: 编辑模式 插入模式 命令模式 |命令|作用| |--|--| |a|在光标字符后插入| |A|在光标行尾插入| |i|在光标字符前插入| |I|在光标行首插入| |o|在光标下 ...