洛谷T21776 子序列
题目描述
你有一个长度为 nn 的数列 {a_n}{an} ,这个数列由 0,10,1 组成,进行 mm 个的操作:
1~l~r1 l r :把数列区间 [l, r][l,r] 内的所有数取反。即 00 变成 11 ,11 变成 00 。
2~l~r2 l r :询问数列在区间 [l, r][l,r] 内共有多少个本质不同的子序列。
输入输出格式
输入格式:
第一行包含两个整数 n, mn,m ,意义如上所述。
接下来一行包含 nn 个数,表示数列 {a_n}{an} 。
接下来 mm 行,每行包含三个数,表示一个操作,操作格式如上所述。
输出格式:
对于每个询问,输出答案模 10^9 + 7109+7 的结果。
输入输出样例
输入样例#1:
4 4
1 0 1 0
2 1 4
2 2 4
1 2 3
2 1 4
输出样例#1:
11
6
8
说明
对于 10 %10% 的数据,1 leq n, m leq 10^21≤n,m≤102 。
对于 30 %30% 的数据,1 leq n, m leq 10^31≤n,m≤103 。
对于 100 %100% 的数据,1 leq n, m leq 10^51≤n,m≤105 。
这道题同HDU6155(只不过我在HDU上T飞了)
首先我们考虑一下暴力怎么写
dp[i][1]表示到第i个位置,以1结尾,本质不同的子序列
dp[i][0]表示到第i个位置,以0结尾,本质不同的子序列
转移的时候,假设第i个字符是1
那么对它有贡献的是以前以0结尾的子序列,以及以前以1结尾的子序列,以及空串
那么此时
dp[i][1]=dp[i-1][0]+dp[i-1][1]+1
dp[i][0]=dp[i-1][0]
当第i个字符是0的时候同理,不难得到
dp[i][1]=dp[i-1][1]
dp[i][0]=dp[i-1][0]+dp[i-1][1]+1
大家有没有发现一件事情?
这个dp的转移是递推!也就是说我们可以用矩阵乘法来加速!
而矩阵乘法可以用线段树来维护!
它的矩阵为
对于操作1的话,先交换要改变的矩阵的第一行和第二行,再交换要改变的矩阵的第一列和第二列
至于为什么?这个可以转移之间的关系入手,也可以直接找规律
这样就实现了两个矩阵的转换
另外还有一点、
对于结果矩阵,我们只会用到[3][1]和[3][2]这两项(分别代表dp[n][1],dp[n][0])
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long int
#define ls k<<1
#define rs k<<1|1
using namespace std;
const int MAXN=1e6+10;
const int mod=1e9+7;
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
char c[MAXN];
struct Matrix
{
LL mat[4][4];
Matrix(){memset(mat,0,sizeof(mat));}
};
struct node
{
int l,r,w;
bool f;
Matrix m;
}T[MAXN];
Matrix zero,one,HHHHH;
Matrix rev(Matrix &a)
{
for(LL i=1;i<=3;i++) swap(a.mat[1][i],a.mat[2][i]);
for(LL i=1;i<=3;i++) swap(a.mat[i][1],a.mat[i][2]);
}
Matrix MatrixMul(Matrix a,Matrix b)
{
Matrix c;
for(LL k=1;k<=3;k++)
for(LL i=1;i<=3;i++)
for(LL j=1;j<=3;j++)
c.mat[i][j]=(c.mat[i][j]+(a.mat[i][k]*b.mat[k][j])%mod )%mod;
return c;
}
void update(int k)
{
T[k].m=MatrixMul(T[ls].m,T[rs].m);
}
void pushdown(int k)
{
if(T[k].f)
{
T[ls].f^=1;T[rs].f^=1;
rev(T[ls].m);rev(T[rs].m);
T[k].f=0;
}
}
void Build(int k,int ll,int rr)
{
T[k].l=ll;T[k].r=rr;T[k].f=0;
if(ll==rr)
{
if(c[ll]=='0') T[k].m=zero;
else T[k].m=one;
return ;
}
int mid=ll+rr>>1;
Build(ls,ll,mid);
Build(rs,mid+1,rr);
update(k);
}
void IntervalChange(int k,int ll,int rr)
{
if(ll<=T[k].l&&T[k].r<=rr)
{
T[k].f^=1;
rev(T[k].m);
return ;
}
pushdown(k);
int mid=T[k].l+T[k].r>>1;
if(ll<=mid) IntervalChange(ls,ll,rr);
if(rr>mid) IntervalChange(rs,ll,rr);
update(k);
}
Matrix IntervalAsk(int k,int ll,int rr)
{
Matrix ans=HHHHH;
if(ll<=T[k].l&&T[k].r<=rr)
{
ans=T[k].m;
return ans;
}
pushdown(k);
LL mid=T[k].l+T[k].r>>1;
if(ll<=mid)
ans=MatrixMul(IntervalAsk(ls,ll,rr),ans);
if(rr>mid)
ans=MatrixMul(ans,IntervalAsk(rs,ll,rr));
return ans;
}
int main()
{
int N,M;
zero.mat[1][1]=zero.mat[2][1]=zero.mat[3][1]=zero.mat[2][2]=zero.mat[3][3]=1;
one.mat[1][1]=one.mat[1][2]=one.mat[2][2]=one.mat[3][2]=one.mat[3][3]=1;
HHHHH.mat[1][1]=HHHHH.mat[2][2]=HHHHH.mat[3][3]=1;
int T;
T=1;
while(T--)
{
N=read();M=read();
scanf("%s",c+1);
Build(1,1,N);
while(M--)
{
int opt=read(),l=read(),r=read();
if(opt==1)
{
IntervalChange(1,l,r);
}
else if(opt==2)
{
Matrix ans=IntervalAsk(1,l,r);
printf("%lldn", (ans.mat[3][1]+ans.mat[3][2])%mod );
}
}
}
return 0;
}
- ASP.NET MVC 2示例Tailspin Travel UI层分析
- CSS 命名之Dialog, Modal, Popup, Popover, Lightbox 等的区别
- Eclipse JAVA文件注释乱码
- 2018年小程序的红利趋势预测,懂的来……或许你将成为下个富翁
- VUE 入门基础(6)
- 五年换4高管,6000员工裁95%剩300人,王健林为何抛弃万达网科?
- Android Permission中英对照
- 你知道人脸识别技术是如何实现的吗?
- WordPress REST API 定制化输出
- ASP.NET MVC的Action Filter
- Android LayoutInflater详解
- 在Android中实现service动态更新UI界面
- VUE 入门基础(5)
- Android的UI设计与后台线程交互
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 关于Java使用groupingBy分组数据乱序问题
- 详解 java CompletableFuture
- 从一个问题来解释下什么是mysql的可重复读
- 每个人都用得到的频数分布直方图
- 利用箱线图巧剔异常值
- dubbo服务接口设计的几个建议
- 使用kafka连接器迁移mysql数据到ElasticSearch
- 这可能是讲雪花算法最全的文章
- 带你了解控制线程执行顺序的几种方法
- 一文说透访问者模式
- 从一个生产上的错误看kafka的消费再均衡问题
- map和object相互转换的几种方法和对比
- Elasticsearch java API客户端介绍
- 如何优雅的判断一个对象的属性是否全部为空
- 内存分析工具MAT的使用入门