[CQOI2018]交错序列
嘟嘟嘟
要是求交错序列的个数和就好了,那我一秒就能切。
换成这个,我就不会了。
我一直想枚举1的个数,然后算出在长度为\(n\)的序列里,有多少个合法的序列,然后又觉得这好像是什么插板法,但是每一个盒子里必须有球,还不会。查了一下发现这东西\(O(1)\)还求不了,于是彻底放弃了。
正解是这样的,首先还得稍微推一下式子。
\]
然后利用二项式定理
\]
于是我们发现,只用求\(y\)的和的\(x\)次幂就行了。
怎么求咧,这时候就要用dp了。
令\(dp[i][j][0 / 1]\)表示长度为\(i\)的序列中,这一位填0 / 1时1的个数的\(j\)次幂。那么分情况转移:
当这一位填0时,1的个数不变,则\(dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1]\)。
当这一位填1时,考虑\(y\)加了1,则\((y + 1) ^ j = \sum _ {k = 0} ^ {j} C_{j} ^ {k} y ^ j\),于是有\(dp[i][j][1] = \sum _ {k = 0} ^ {j} C_{j} ^ {k} dp[i - 1][j][0]\)
讲真这时候维护前缀和\(O(n)\)dp应该已经能过了,但是交上去就是TLE,所以只能改成矩乘了(还得卡常)。
矩阵是一个边长为\(2(a + b)\)的正方形矩阵,用样例一构造后的矩阵张这个样子:
1 0 0 0 1 0 0 0
0 1 0 0 0 1 0 0
0 0 1 0 0 0 1 0
0 0 0 1 0 0 0 1
1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0
1 2 1 0 0 0 0 0
1 3 3 1 0 0 0 0
总结一下,感觉上点难度的dp题有一部分就是在dp之前先要推一推式子,推到觉得可以dp的时候再开始dp。至于啥时候觉得可以dp,估计这就得靠刷题吧……
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 205;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, a, b, mod, Max;
struct Mat
{
ll a[maxn][maxn];
In Mat operator * (const Mat& oth)const
{
static Mat ret; Mem(ret.a, 0);
for(int i = 0; i <= Max; ++i)
for(int j = 0; j <= Max; ++j)
{
for(int k = 0; k <= Max; ++k) ret.a[i][j] += a[i][k] * oth.a[k][j];
ret.a[i][j] %= mod;
}
return ret;
}
}f;
ll C[maxn][maxn];
In void init()
{
for(int i = 0; i <= a + b; ++i) C[i][0] = 1;
for(int i = 1; i <= a + b; ++i)
for(int j = 1; j <= i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
Mem(f.a, 0); Max = ((a + b) << 1) + 1;
for(int i = 0; i <= a + b; ++i) f.a[i][i] = f.a[i][a + b + 1 + i] = 1;
for(int i = 0; i <= a + b; ++i)
for(int j = 0; j <= i; ++j) f.a[a + b + 1 + i][j] = C[i][j];
}
In Mat quickpow(Mat A, ll b)
{
Mat ret; Mem(ret.a, 0);
for(int i = 0; i <= Max; ++i) ret.a[i][i] = 1;
for(; b; b >>= 1, A = A * A)
if(b & 1) ret = ret * A;
return ret;
}
ll ans[maxn];
int main()
{
n = read(), a = read(), b = read(), mod = read();
init();
Mat A = quickpow(f, n);
for(int i = 0; i <= a; ++i)
{
for(int j = 0; j <= Max; ++j) ans[i] += A.a[i][j];
for(int j = 0; j <= Max; ++j) ans[i] += A.a[a + b + 1 + i][j];
ans[i] %= mod;
}
ll Ans = 0, tp = 1;
for(int i = 0, flg = pow(-1, a); i <= a; ++i, tp = tp * n % mod, flg *= (-1))
Ans = (Ans + C[a][i] * tp % mod * (A.a[a + b - i][0] + A.a[a + b - i + a + b + 1][0]) * flg % mod + mod) % mod;
write(Ans), enter;
return 0;
}
[CQOI2018]交错序列的更多相关文章
- 【BZOJ5298】[CQOI2018]交错序列(动态规划,矩阵快速幂)
[BZOJ5298][CQOI2018]交错序列(动态规划,矩阵快速幂) 题面 BZOJ 洛谷 题解 考虑由\(x\)个\(1\)和\(y\)个\(0\)组成的合法串的个数. 显然就是把\(1\)当做 ...
- [CQOI2018]交错序列 (矩阵快速幂,数论)
[CQOI2018]交错序列 \(solution:\) 这一题出得真的很好,将原本一道矩阵快速幂硬生生加入组合数的标签,还那么没有违和感,那么让人看不出来.所以做这道题必须先知道(矩阵快速幂及如何构 ...
- BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*
BZOJ5298 CQOI2018 交错序列 [DP+矩阵快速幂优化] Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻 ...
- bzoj 5298: [Cqoi2018]交错序列
Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻的0).例如,000,001 ,101,都是交错序列,而110则不是.对 ...
- BZOJ5298 [CQOI2018] 交错序列 | 矩阵乘法和一个trick
题面 求所有长度为\(n\)的.没有相邻的1的01序列中,若0有\(x\)个.1有\(y\)个,\(x^ay^b\)之和(对\(m\)取模). \(n \le 10^7, m \le 10^8, 0 ...
- BZOJ5298 CQOI2018交错序列(动态规划+矩阵快速幂)
显然答案为Σkb·(n-k)a·C(n-k+1,k).并且可以发现ΣC(n-k,k)=fibn.但这实际上没有任何卵用. 纯组合看起来不太行得通,换个思路,考虑一个显然的dp,即设f[i][j][0/ ...
- [BZOJ5298][CQOI2018]交错序列(DP+矩阵乘法)
https://blog.csdn.net/dream_maker_yk/article/details/80377490 斯特林数有时并没有用. #include<cstdio> #in ...
- 【[CQOI2018]交错序列】
这个题简直有毒,\(O((a+b)^3logn)\)的做法不卡常只比\(O(2^n*n)\)多\(10\)分 看到\(a\)和\(b\)简直小的可怜,于是可以往矩阵上联想 发现这个柿子有些特殊,好像可 ...
- cqoi2018
题解: 很多模板题 第一次写莫队还比较顺利 除了把排序的cmp写错..(还第一次遇到) 这题分块也可以 先预处理出g[i][j]代表前i个块,颜色为j的有多少种 f[i][j]表示i-j的块能构成多少 ...
随机推荐
- vbscript 语言通过序列和ADODB实现取号不重复
目的:通过VBScript脚本利用序列的性质,实现取号不重复 首先,表空间中创建表名为TABLE_YEWID的表格,主要有以下几个字段 -- Create table create table TAB ...
- 洛谷P2286 [HNOI2004]宠物收养场
题目描述 凡凡开了一间宠物收养场.收养场提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物. 每个领养者都希望领养到自己满意的宠物,凡凡根据领养者的要求通过他自己发明的一个特殊的公式,得出该领 ...
- 洛谷P4577 [FJOI2018]领导集团问题(dp 线段树合并)
题意 题目链接 Sol 首先不难想到一个dp,设\(f[i][j]\)表示\(i\)的子树内选择的最小值至少为\(j\)的最大个数 转移的时候维护一个后缀\(mx\)然后直接加 因为后缀max是单调不 ...
- 什么是CSR以及CSR的作用和生成
什么是CSR以及CSR的作用和生成 来源:https://www.trustasia.com/news-201801-what-is-the-role-and-generation-of-csr-an ...
- Python 标准类库-数据类型之copy-深拷贝浅拷贝操作
标准类库-数据类型之copy-深拷贝浅拷贝操作 by:授客 QQ:1033553122 Python中赋值并不会拷贝对象,只是创建目标和对象的绑定关系. copy.copy(x) 返回x的浅拷贝 ...
- mysql数据表的基本操作:表结构操作,字段操作
本节介绍: 表结构操作 创建数据表. 查看数据表和查看字段. 修改数据表结构 删除数据表 字段操作 新增字段. 修改字段数据类型.位置或属性. 重命名字段 删除字段 首发时间:2018-02-18 ...
- LeetCode题解之Binary Tree Right Side View
1.题目描述 2.问题分析 使用层序遍历 3.代码 vector<int> v; vector<int> rightSideView(TreeNode* root) { if ...
- SQL 中用户定义函数的使用方法
--用户定义函数的分类: /* 1.标量函数 2.表值函数 2.1内联表值函数 返回单个SELECT语句, 它没有相关的返回变量和函数体 2.2多语句表值函数 是视图和存储过程的结合 可嵌套 */ ...
- MongoDB Sharding分片配置
Ps:mongod是mongodb实例,mongos被默认为为mongodb sharding的路由实例. 本文使用的mongodb版本为3.2.9,因此参考网址为:https://docs.mong ...
- Linux CFS调度器之负荷权重load_weight--Linux进程的管理与调度(二十五)
1. 负荷权重 1.1 负荷权重结构struct load_weight 负荷权重用struct load_weight数据结构来表示, 保存着进程权重值weight.其定义在/include/lin ...