洛谷 [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]\) ...
随机推荐
- iOS面试题 第一天
今天上午,下午分别面试了两家公司.上午是一家互联网公司,气氛还比较好,是我比较喜欢的.技术这块是直接机试,主要是给了些BUG让我修复,整个过程还算顺利.下午去了一家大型的证券公司.整理技术问题如下: ...
- 【工具篇】在.Net中实现HTML生成图片或PDF的几种方式
前段时间由于项目上的需求,要在.Net平台下实现把HTML内容生成图片或PDF文件的功能,特意在网上研究了几种方案,这里记录一下以备日后再次使用.当时想着找一种开发部署都比较清爽并且运行稳定的方案,但 ...
- 用例重试机制rerunfailures
安装 rerunfailures插件 pip install pytest-rerunfailures 使用: pytest --reruns 重试次数 如:pytest --reruns 2 重 ...
- 汇编segment
一个正常的应用程序被由若干个 segment组成. 定义 segment: SECTION .段名 SECTION 也可以小写 如: 定义数据段: section .data 定义代码段: sec ...
- CAD交互绘制圆形云线批注(网页版)
js中实现代码说明: 动态拖放时的绘制事件: function DoDynWorldDrawFun(dX,dY,pWorldDraw,pData) { //自定义实体的GUID标识符 var sGui ...
- python_112_网络编程 Socket编程
实例1:客户端发小写英文,服务器端返回给客户端大写英文(仅支持一次接受发送) 服务器端: #服务器端(先于客户端运行) import socket server=socket.socket() ser ...
- Mybatis generator自动生成代码包括实体,dao,xml文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration ...
- dhtmlTree简单实例以及基本参数设置
demo实例参考: <link rel="STYLESHEET" type="text/css" href="css/dhtmlXTree.c ...
- [九省联考2018] IIIDX 线段树+贪心
题目: 给出 k 和 n 个数,构造一个序列使得 d[i]>=d[i/k] ,并且字典序最大. 分析: 听说,当年省选的时候,这道题挡住了大批的高手,看上去十分简单,实际上那道弯段时间内是转不过 ...
- 【OS_Linux】Linux中虚拟机的三种上网方式——桥接、NAT、Host-only
1.桥接 桥接方便做实验,配置ip方便.可以和局域网中的其他机器进行通信,也可以和公网进行通信.缺点是会占用主机所在局域网的一个ip. 2. NAT NAT模式下虚拟机可以和主机进行通信,可以上网,而 ...