洛谷 [T21776] 子序列
题目描述
你有一个长度为 \(n\) 的数列 \(\{a_n\}\) ,这个数列由 \(0,1\) 组成,进行 \(m\) 个的操作:
\(1\ l\ r\) :把数列区间$ [l,r]$ 内的所有数取反。即 \(0\) 变成 \(1\) ,\(1\) 变成 \(0\) 。
\(2\ l\ r\) :询问数列在区间 \([l, r]\) 内共有多少个本质不同的子序列。
输入输出格式
输入格式:
第一行包含两个整数 \(n,m\),意义如上所述。
接下来一行包含 \(n\) 个数,表示数列 \(\{a_n\}\) 。
接下来 \(m\) 行,每行包含三个数,表示一个操作,操作格式如上所述。
输出格式:
对于每个询问,输出答案模 \(10^{9}+7\) 的结果。
思路
前置技能:
维护一个长度为 \(n\) 的 \(3*3\) 的 \(0/1\) 矩阵序列
交换区间 \([l,r]\) 中所有矩阵的第一行和第二行
查询区间 \([l,r]\) 中所有矩阵从左到右乘起来的结果
对于能快速合并的信息我们都可以用线段树来维护
比如和,积,最值, 矩阵乘法, bitset, hash值,线性基
还需要一个矩阵的结论:
对于 3*3 的 0/1 矩阵来说 两矩阵的第一二行交换,他们的乘积的第一二行也交换
所以可以对于交换的区间打 tag,用线段树维护
本题思路
考虑本质不同的子序列怎么求:
设 f(i,0) 表示i号位置以前的以0结尾的本质不同的子序列数目
设 f(i,1) 表示i号位置以前的以1结尾的本质不同的子序列数目
转移方程 :
如果 i 号位置是 0 ,\(f(i,0) = f(i-1,0) + f(i-1,1) + 1 ; f(i, 1) = f(i-1, 1)\)
如果 i 号位置是 1 ,\(f(i,1) = f(i-1,0) + f(i-1,1) + 1 ; f(i, 0) = f(i-1, 0)\)
用矩阵加速,可得:

观察矩阵可得,对区间内序列取反,可以转化为把矩阵的前两行,前两列交换
可用线段树来维护
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
using namespace std;
const int MOD = 1e9 + 7;
int init() {
int rv = 0, fh = 1;
char c = getchar();
while(c <'0' || c > '9') {
if(c == '-') fh = -1;
c=getchar();
}
while(c >= '0' && c <= '9') {
rv=(rv<<1) + (rv<<3) + c- '0';
c = getchar();
}
return fh * rv;
}
const int MAXN=100005;
struct Matrix{
ll num[3][3];
int col;
Matrix() {
col = 0;
memset(num,0,sizeof(num));
}
void build(bool f){
col=3;
if(f) {
num[0][0] = num[0][1] = num[1][1] = num[2][1] = num[2][2] = 1;
}else {
num[0][0] = num[1][0] = num[2][0] = num[1][1] = num[2][2] = 1;
}
}
Matrix operator * (const Matrix &a) {
Matrix ans;
ans.col = col;
for(int i = 0 ; i < col ; i++) {
for(int j = 0 ; j < 3 ; j++) {
for(int k = 0 ; k < 3 ; k++) {
(ans.num[i][j] += num[i][k] * a.num[k][j]) %= MOD;
}
}
}
return ans;
}
void reserve() {
for(int i = 0 ; i < 3 ; i++) {
swap(num[0][i],num[1][i]);
}
for(int i = 0 ; i <3 ; i++) {
swap(num[i][0], num[i][1]);
}
}
void print() {
for(int i = 0 ; i<=col ;i++) {
for(int j = 0 ; j < 3 ; j++) {
printf("%d ",num[i][j]);
}
cout<<endl;
}
}
};
struct SGT{
Matrix sum[MAXN<<2];
bool tag[MAXN<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] * sum[rt<<1|1];
}
void build(int l, int r,int rt) {
if(l==r) {
bool f=init();
sum[rt].build(f);
return;
}
int mid = (l + r) >>1;
build(lson);
build(rson);
PushUp(rt);
}
void PushDown(int rt) {
if(tag[rt]) {
tag[rt<<1] = !tag[rt<<1] ;
tag[rt<<1|1] = !tag[rt<<1|1];
sum[rt<<1].reserve();
sum[rt<<1|1].reserve();
tag[rt]=0;
}
}
void Update(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
tag[rt]=!tag[rt];
sum[rt].reserve();
return;
}
PushDown(rt);
int mid = (l + r) >>1;
if(L <= mid) Update(L, R, lson);
if(mid < R) Update(L, R, rson);
PushUp(rt);
}
Matrix Query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
return sum[rt];
}
PushDown(rt);
int mid = (l + r) >>1;
Matrix ans;
ans.col = 3;
ans.num[0][0] = ans.num[1][1] = ans.num[2][2] = 1;//ans.print();
if(L <= mid) ans = ans * Query(L, R, lson);
if(mid < R) ans = ans * Query(L, R, rson);
PushUp(rt);
return ans;
}
}sgt;
int n,m;
int main() {
freopen("in.txt", "r", stdin);
n=init();m=init();
sgt.build(1,n,1);
for(int i = 1 ; i <= m ; i++) {
int t = init(), l = init(), r = init();
if(t == 1) {
sgt.Update(l, r, 1, n, 1);
}else {
Matrix ans;
ans.col = 1;ans.num[0][2] = 1;
ans = ans * sgt.Query(l, r, 1, n, 1);
//sgt.Query(l, r, 1, n, 1).print();
printf("%lld\n",(ans.num[0][0] + ans.num[0][1])%MOD);
}
}
fclose(stdin);
return 0;
}
洛谷 [T21776] 子序列的更多相关文章
- 洛谷T21776 子序列
题目描述 你有一个长度为 nn 的数列 \{a_n\}{an} ,这个数列由 0,10,1 组成,进行 mm 个的操作: 1~l~r1 l r :把数列区间 [l, r][l,r] 内的所有数取反. ...
- 洛谷P1410 子序列
题目描述 给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列, 输入输出格式 输入格式: 若干行,每行表示一组数据.对于每组数据,首先输入一个整数N,表示序列的长度. ...
- 洛谷 P1410 子序列(DP)
这题的题解的贪心都是错误的...正解应该是个DP 考虑有哪些有关的条件:两个序列的当前长度, 两个序列的末尾数, 把这些都压进状态显然是会GG的 考虑两个长度加起来那一位的数一定是其中一个序列的末尾, ...
- 洛谷 P1439 【模板】最长公共子序列
\[传送门啦\] 题目描述 给出\(1-n\)的两个排列\(P1\)和\(P2\),求它们的最长公共子序列. 输入输出格式 输入格式: 第一行是一个数\(n\), 接下来两行,每行为\(n\)个数,为 ...
- 洛谷CF264D Colorful Stones(子序列匹配,思维)
洛谷题目传送门 神仙思维题. 对于两个字符串的匹配问题,似乎之前蒟蒻写的HAOI2010最长公共子序列题解中提到的建网格图模型是一种套路? 给一个稍微强一点的样例(把字母换成了ABC) AABCB B ...
- 洛谷P2516 [HAOI2010]最长公共子序列(LCS,最短路)
洛谷题目传送门 一进来就看到一个多月前秒了此题的ysn和YCB%%% 最长公共子序列的\(O(n^2)\)的求解,Dalao们想必都很熟悉了吧!不过蒟蒻突然发现,用网格图貌似可以很轻松地理解这个东东? ...
- 洛谷1439:最长公共子序列(nlogn做法)
洛谷1439:最长公共子序列(nlogn做法) 题目描述: 给定两个序列求最长公共子序列. 这两个序列一定是\(1\)~\(n\)的全排列. 数据范围: \(1\leq n\leq 10^5\) 思路 ...
- 最长公共子序列问题(LCS) 洛谷 P1439
题目:P1439 [模板]最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 关于LCS问题,可以通过离散化转换为LIS问题,于是就可以使用STL二分的方法O(nlogn ...
- 洛谷P4608 [FJOI2016]所有公共子序列问题 【序列自动机 + dp + 高精】
题目链接 洛谷P4608 题解 建个序列自动机后 第一问暴搜 第二问dp + 高精 设\(f[i][j]\)为两个序列自动机分别走到\(i\)和\(j\)节点的方案数,答案就是\(f[0][0]\) ...
随机推荐
- CloudFoundry命令行和Kubernetes命令行的Restful API消费方式
先说CloudFoundry的命令行工具CLI.我们在CloudFoundry环境下工作,第一个使用的命令就是cf login. 如果在环境变量里维护CF_TRACE的值为true: 则我们能发现,诸 ...
- libxml2.dylb 导致<libxml/tree.h> 老是找不到头文件
添加了libxml2.dylb的framework ,结果还是引用不了<libxml/tree.h>, 老是提示找不到头文件. 这个问题其实比较容易解决,但是XCode的版本问题确实让开 ...
- Linux文件的IO操作 一
系统调用 系统调用: 操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务 为什么用户程序不能直接访问系统内核提供的服务 为了更好地保护内核空间, ...
- python之文件读写操作(r/r+/rb/w/w+/wb/a/a+/ab)的作用
'r':只读.该文件必须已存在. 'r+':可读可写.该文件必须已存在,写为追加在文件内容末尾. 'rb':表示以二进制方式读取文件.该文件必须已存在. 'w':只写.打开即默认创建一个新文件,如果文 ...
- 使用gcc -g编译,gdb调试时仍然存在“no debug symbols found”的错误
今天为调试一段代码,使用gcc将程序用-g选项重新编译.但是使用gdb进行debug时,仍然出现“no debug symbols found”的错误.仔细检查了一下Makefile,原来后面定义的连 ...
- shell脚本,一个经典题目。
[root@localhost wyb]# cat zhuijiu.sh #!/bin/bash #.写一个脚本执行后,输入名字,产生随机数01-99之间的数字. #.如果相同的名字重复输入,抓到的数 ...
- shell脚本,计算从0+2+4+6+....100的结果是多少?
[root@localhost wyb]# cat evenjia.sh #!/bin/bash #从0++++...100的结果 i= ` do sum=$(($sum+i)) i=$(($i+)) ...
- 17条 Swift 最佳实践规范
本文由CocoaChina译者小袋子(博客)翻译自schwa的github主页原文作者:schwa 这是一篇 Swift 软件开发的最佳实践教程. 前言 这篇文章是我根据在 SwiftGraphics ...
- Redis string类型常用操作
Redis 有 string.list.set.zset.hash数据类型.string类型是最基础的,其他类型都是在string类型上去建立的,所以了解熟悉string类型的常用操作对于学习re ...
- Centos忘记密码解决方法
centos6.8忘记root密码解决方法 重启系统后出现GRUB界面在引导装载程序菜单上,用上下方向键选择你忘记密码的那个系统键入"e" 来进入编辑模式. 接下来你可以看到如下图 ...