CF1151F Sonya and Informatics (计数dp+矩阵优化)
题目地址
Solution
(duyi是我们的红太阳)
(这里说一句:这题看上去是一个概率dp,鉴于这题的概率dp写法看上去不好写,我们其实可以写一个计数dp)
首先拿到这个题目我们要能设出一个普通dp。难点在于状态如何设计。(n<=100)状态压缩不可行。
这里有一个设计状态的套路:因为这是一个01序列,最终不降序的状态不就是0全部在前面,1全部在后面吗?设一共有c个0,把这个序列分成[1,c],[c+1,n]左右两个部分,我们假设当前序列左边有t个0,这样序列左边就有c-t个1,序列右边就有c-t个0,序列右边就有n-c-c+t个1,这样我们只要知道序列左边有多少个0,整个序列的情况就被我们刻画出来了(妙啊!)。
我们设 f[i,j] 表示 i 次变化后左边[1,c]区间有j个零的方案总数。假设开始有左边有t个0
初始化 : \(f[0,t]=1\)
最终答案:
\]
f[i,j] 转移可以从 f[i-1,j-1] , f[i-1,j] , f[i-1,j+1] 三个地方转移过来。
f[i-1,j-1] => f[i,j] 左边的0变多了,说明是左边的1和右边的0交换了,有 (左1 * 右0)种情况。
f[i-1,j] => f[i,j] 左边的0没有变,可能是左边的数内部交换,右边的数内部交换,左边的0和右边的0交换,左边的1和右边的1交换,情况自己算。
f[i-1,j+1] => f[i,j] 左边的0变少了,说明是左边的0和右边的1换了,情况自己算。
于是我们写出了转移方程,发现每次转移都是由 f[i-1, ] 转移而来,所以我们可以考虑矩阵优化这个dp
开一个 \(1*c+1\) 的矩阵 \(F\) 下标从 \([0,c]\) ,\(F[1,j]\) 表示 \(f[i,j]\)。
写一个状态转移矩阵 \(A\),使得 \(F * A = F`\) ,\(F`[i,j]\) 表示 \(f[i+1,j]\)
\(A\)怎么构建 这部分就很简单了,留给读者自主思考。
Code
Talk is cheap.Show me the code.
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x=0,f=1; char ch=getchar();
while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
const int N = 107,MOD = 1e9+7;
int n,K,c;
bool s[N];
struct Mat {
int val[N][N]; int Max_n, Max_m;
Mat() {
memset(val, 0, sizeof(val)); Max_n = Max_m = 0;
}
Mat operator * (const Mat el) const {
Mat c;
for(int i=0;i<=Max_n;++i) //此题比较特殊 从零开始计数
for(int j=0;j<=el.Max_m;++j)
for(int k=0;k<=Max_m;++k)
c.val[i][j] = (c.val[i][j]+((val[i][k]*el.val[k][j])%MOD))%MOD;
c.Max_n = Max_n, c.Max_m = el.Max_m;
return c;
}
inline void I(int n) {
Max_n = Max_m = n;
for(int i=0;i<=n;++i) val[i][i] = 1;
}
};
inline int power(int x,int y) {
int res = 1, base = x;
while(y) {
if(y&1) res = (res*base)%MOD; base = (base*base)%MOD; y >>= 1;
}
return res;
}
signed main()
{
n = read(), K = read();
for(int i=1;i<=n;++i) s[i] = read(), c += (s[i]==0);
Mat A,F; int t = 0; //记录c区间以左有多少个0
for(int i=1;i<=c;++i) if(s[i]==0) ++t;
F.val[0][t] = 1; F.Max_n = 1, F.Max_m = c; // f[j] 表示 [0,j] 的总次数
for(int i=0;i<=c;++i) {
if(i != 0) A.val[i-1][i] = (c-i+1)*(c-i+1);
A.val[i][i] = (c*(c-1)/2) + ((n-c)*(n-c-1)/2) + (i*(c-i)) + (c-i)*(n-c-c+i);
if(i != c) A.val[i+1][i] = (i+1)*(n-c-c+i+1);
}
A.Max_n = A.Max_m = c;
int y = K; Mat res; res.I(c);
while(y) {
if(y&1) res = res*A; A = A*A; y >>= 1;
}
F = F * res;
int ans = F.val[0][c], sum = 0;
for(int i=0;i<=c;++i) sum = (sum+F.val[0][i])%MOD;
ans = (ans * power(sum,MOD-2))%MOD;
printf("%lld\n",ans);
return 0;
}
Summary
这个题目精华就在于dp状态设计这里,没有使用状压而是用一个数刻画了整个局面。
这种做法可以推广到01序列的其他操作,只要保证最终状态和目前状态的区别很小,就可以考虑记录0,1的差异,由此可以设计出dp状态。
CF1151F Sonya and Informatics (计数dp+矩阵优化)的更多相关文章
- hdu 4576(简单概率dp | 矩阵优化)
艰难的一道题,体现出菜菜的我... 首先,先吐槽下. 这题到底出题人是怎么想的,用普通概率dp水过??? 那为什么我概率dp写的稍微烂点就一直tle? 感觉很不公平.大家算法都一致,因为我程序没有那 ...
- Codeforces 917C - Pollywog(状压 dp+矩阵优化)
UPD 2021.4.9:修了个 typo,为啥写题解老出现 typo 啊( Codeforces 题目传送门 & 洛谷题目传送门 这是一道 *2900 的 D1C,不过还是被我想出来了 u1 ...
- New Year and Old Subsequence CodeForces - 750E (dp矩阵优化)
大意: 给定字符串, 每次询问区间[l,r]有子序列2017, 无子序列2016所需要删除的最小字符数 转移用矩阵优化一下, 要注意$(\mathbb{Z},min,+)$的幺元主对角线全0, 其余全 ...
- BZOJ4000 [TJOI2015]棋盘 【状压dp + 矩阵优化】
题目链接 BZOJ4000 题解 注意题目中的编号均从\(0\)开始= = \(m\)特别小,考虑状压 设\(f[i][s]\)为第\(i\)行为\(s\)的方案数 每个棋子能攻击的只有本行,上一行, ...
- [Vijos1067]Warcraft III 守望者的烦恼(DP + 矩阵优化)
传送门 可知 f[i] = f[i - 1] + f[i - 2] + ... + f[i - k] 直接矩阵优化就好了 #include <cstdio> #include <cs ...
- CF1151F Sonya and Informatics(概率期望,DP,矩阵快速幂)
明明是水题结果没切掉……降智了…… 首先令 $c$ 为序列中 $0$ 的个数,那么排序后序列肯定是前面 $c$ 个 $0$,后面 $n-c$ 个 $1$. 那么就能上 DP 了.(居然卡在这里……) ...
- 【BZOJ4818】[Sdoi2017]序列计数 DP+矩阵乘法
[BZOJ4818][Sdoi2017]序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数 ...
- loj#2002. 「SDOI2017」序列计数(dp 矩阵乘法)
题意 题目链接 Sol 质数的限制并没有什么卵用,直接容斥一下:答案 = 忽略质数总的方案 - 没有质数的方案 那么直接dp,设\(f[i][j]\)表示到第i个位置,当前和为j的方案数 \(f[i ...
- bzoj1494 生成树计数 (dp+矩阵快速幂)
题面欺诈系列... 因为一个点最多只能连到前k个点,所以只有当前的连续k个点的连通情况是对接下来的求解有用的 那么就可以计算k个点的所有连通情况,dfs以下发现k=5的时候有52种. 我们把它们用类似 ...
随机推荐
- Mysql 创建函数出现This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary mys ...
- Fresnel integral菲涅尔积分的一丢丢探讨
起因源于导师的关于回旋曲线的一点问题 其中最后得到的曲率公式中的c,s’和s定义不明确 于是开始从头从(2.1)式中的积分入手探究 维基百科中Fresnel integral的S(x)与C(x)的定义 ...
- 编译mysql时CMake Error at cmake/readline.cmake:85 (MESSAGE)
CMake Error at cmake/readline.cmake:85 (MESSAGE): Curses library not found. Please install appropr ...
- python学习笔记:(六)str(字符串)常用方法
注意点: 1.字符串是不可变的: 2.%格式化操作符:左侧放置字符串,右侧放置希望被格式化的值. 对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应 ...
- python基础:multiprocessing的使用
不同于C++或Java的多线程,python中是使用多进程来解决多项任务并发以提高效率的问题,依靠的是充分使用多核CPU的资源.这里是介绍mulitiprocessing的官方文档:https://d ...
- Java ——重写、多态、抽象类
本节重点思维导图 重写 子类覆盖父类同名的方法 final关键字:不可变的 public static final PAGE_SIZE = 18; final修饰的类不能做为父类被子类继承. 多态 多 ...
- 所遇Oracle错误代码
ORA-00905: 缺失关键字 少了空格或关键字写错 ORA-00922: 选项缺失或无效 错误原因:一般是语句的语法有问题.比如命名不对,关键字写错等等.对于非标准的命名,一般采用双引号来创建 ...
- 自定义SpringBoot启动控制台图标
使用过SpringBoot的小伙伴众所周知,在启动的过程中,在控制台会首先打印spring的图标以及版本号(这里以IDEA为例) 如果需要更改这个打印图标的话, 需要以下步骤: 1.打开SpringB ...
- [Git] 021 来一颗“恶魔果实”?
0. 前言 需要新的功能时,一般会新建一条 "feature" 分支(尴尬的是,我第一眼看时,看成了 "future") 在 "feature&quo ...
- Java数据结构之双向链表
管理单向链表的缺点分析: 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找. 单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找 ...