Time Limit: 1000 ms   Memory Limit: 256 MB

Description

  给定一个01串 $S_{1 \cdots n}$ 和 $Q$ 个操作。

  操作有两种类型:

  1、将 $[l, r]$ 区间的数取反(将其中的0变成1,1变成0)。

  2、询问字符串 $S$ 的子串 $S_{l \cdots r}$ 有多少个不同的子序列。由于答案可能很大,请将答案对 $10^9 + 7$ 取模。

  在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。

Input

  第一行包含两个整数 $N$ 和 $Q$ ,分别表示字符串长度和操作次数。

  第二行包含一个字符串 $S$ 。

  接下来 $Q$ 行,每行3个整数 $type, l, r$ ,其中 $type$ 表示操作类型, $l, r$ 表示操作区间为 $[l, r]$ 。

Output

  对于每一个 $type = 2$ 的询问,输出一个整数表示答案。

  由于答案可能很大,请将答案对 $10^9 + 7$ 取模。

  

Sample Input

Sample Output

4 4
1010
2 1 4
2 2 4
1 2 3
2 1 4
11
6
8

HINT

  数据范围与约定

  对于5%的数据, $N \leq 20, Q = 1$

  对于10%的数据, $N \leq 1000, Q = 1$

  对于20%的数据, $N \leq 10^5, Q \leq 10$

  对于另外30%的数据, $1 \leq N \leq 10^5, 1 \leq Q \leq 10^5, type = 2$

  对于100%的数据, $1 \leq N \leq 10^5, 1 \leq Q \leq 10^5$


题解

  这道题很有意思。

  首先考虑一下不带修改的解法。

  设$f_{i,0}$表示$s_1...s_i$中,以$0$结尾的子序列数量;$f_{i,1}$表示$s_1...s_i$中,以$1$结尾的子序列数量。

  则有方程:

    若$s_i$为0:$\begin{aligned}f_{i,0}&=f_{i-1,0}+f_{i-1,1}+1\\f_{i,1}&=f_{i-1,1}\end{aligned}$

         若$s_i$为1:$\begin{aligned}f_{i,0}&=f_{i-1,0}\\f_{i,1}&=f_{i-1,0}+f_{i-1,1}+1\end{aligned}$

  

  发现这是一类线性递推,如果用一个1x3的矩阵表示原来的$f_{i,0}$与$f_{i,1}$:$\begin{pmatrix} f_0&f_1&1 \end{pmatrix}\\$(最后的1仅作为辅助计算),乘上一个3x3的转移矩阵来得到下一位的状态呢?

  如果序列中这一位$s_i$为0,则在后面乘上这样一个转移矩阵$G_0$:

  $$\begin{pmatrix} f_0&f_1&1\end{pmatrix}*\begin{pmatrix} 1&0&0\\1&1&0\\1&0&1 \end{pmatrix}=\begin{pmatrix} f_0+f_1+1&f_1&1\end{pmatrix}$$

  如果这一位$s_i$为1,则在后面乘上另一个转移矩阵$G_1$:

  $$\begin{pmatrix} f_0&f_1&1\end{pmatrix}*\begin{pmatrix} 1&1&0\\0&1&0\\0&1&1 \end{pmatrix}=\begin{pmatrix} f_0&f_0+f_1+1&1\end{pmatrix}$$

  那么我们用线段树存储每一位的转移矩阵,查询时直接查询$[l,r]$的矩阵乘积,乘上初始矩阵(其实初始矩阵为$(0,0,1)$乘了相当于没乘),所以直接输出查询矩阵的$[3][1]+[3][2]$即可

处理区间数值翻转操作

  最基础的想法就是,将线段树$[l,r]$叶子节点对应的转换矩阵换成另一个转换矩阵。

  观察$G_0=\begin{pmatrix} 1&0&0\\1&1&0\\1&0&1 \end{pmatrix}$            与$G_1=\begin{pmatrix} 1&1&0\\0&1&0\\0&1&1 \end{pmatrix}$

  本质上,只需把第一第二行交换一下,再将第一第二列交换一下,它们都能变成对方。

  第一第二行交换,相当于在$G$前乘上一个矩阵$A$。第一第二列交换,相当于在$G$后乘上这个矩阵$A$。

  $$A=\begin{pmatrix} 0&1&0\\1&0&0\\0&0&1\end{pmatrix}$$

  那么:

    $A*G1*A=G2$       $A*G2*A=G1$

  我们暂且看回原来的模型:计算一个矩阵序列。

  如果要对$[l,r]$的数字翻转,假设矩阵序列是$a*b*c*d*e$,考虑如何变换:

  按照我们的预想处理方式,那应该变成$(A*a*A)*(A*b*A)*(A*c*A)*(A*d*A)*(A*e*A)$。

  此时我们发现,$A*A$居然是单位矩阵...

  于是就变成了$A*(a*b*c*d*e)*A$。

  相当于对$a*b*c*d*e$直接手动1、2行交换,1、2列交换。

  回到线段树,如果要翻转,直接在对应区间维护的矩阵进行 行交换列交换,维护并下传标记即可。

  

  神题啊!


 #include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=,Mod=1e9+;
int n,q;
char in[N];
struct Mat{
ll a[][];
void flip(){
for(int i=;i<;i++) swap(a[i][],a[i][]);
swap(a[][],a[][]);
swap(a[][],a[][]);
}
friend Mat operator * (Mat x,Mat y){
Mat ret;
for(int i=;i<;i++)
for(int j=;j<;j++){
ret.a[i][j]=;
for(int k=;k<;k++)
ret.a[i][j]=(ret.a[i][j]+x.a[i][k]*y.a[k][j])%Mod;
}
return ret;
}
};
const Mat stand[]={{,,,,,,,,},{,,,,,,,,}};
struct SegmentTree{
int root,cnt,ch[N*][],rev[N*];
Mat info[N*];
void build(int &u,int l,int r){
if(!u) u=++cnt;
if(l==r){
info[u]=stand[in[l]==''];
return;
}
int mid=(l+r)>>;
build(ch[u][],l,mid);
build(ch[u][],mid+,r);
pushup(u);
}
void flip(int u,int l,int r,int L,int R){
if(L<=l&&r<=R){
rev[u]^=;
info[u].flip();
return;
}
pushdown(u);
int mid=(l+r)>>;
if(L<=mid) flip(ch[u][],l,mid,L,R);
if(mid<R) flip(ch[u][],mid+,r,L,R);
pushup(u);
}
Mat query(int u,int l,int r,int L,int R){
if(L<=l&&r<=R) return info[u];
pushdown(u);
int mid=(l+r)>>;
if(R<=mid) return query(ch[u][],l,mid,L,R);
if(mid<L) return query(ch[u][],mid+,r,L,R);
return query(ch[u][],l,mid,L,R)*query(ch[u][],mid+,r,L,R);
}
inline void pushup(int u){
info[u]=info[ch[u][]]*info[ch[u][]];
}
inline void pushdown(int u){
if(!rev[u]) return;
rev[ch[u][]]^=; rev[ch[u][]]^=;
info[ch[u][]].flip(); info[ch[u][]].flip();
rev[u]=;
}
}seg;
int main(){
scanf("%d%d%s",&n,&q,in+);
seg.build(seg.root,,n);
int t,l,r;
while(q--){
scanf("%d%d%d",&t,&l,&r);
if(t==)
seg.flip(seg.root,,n,l,r);
else{
Mat ans=seg.query(seg.root,,n,l,r);
printf("%lld\n",(ans.a[][]+ans.a[][])%Mod);
}
}
return ;
}

奇妙代码

最新文章

  1. VS2015 RTM与ASP.NET 5 RC1之坑
  2. Java—数据库技术
  3. PHP_Memcache函数详解
  4. javaEE规范和SSH三大框架到底有什么关系
  5. PureBasic 打开一个一行有多个数据的文件并读取其中某个数据
  6. iphone开发第二个程序
  7. ListView的item中有button和checkbox,listview的点击事件无效
  8. python模块基础之getpass模块
  9. 解析stm32的时钟
  10. ASP.NET Core 实战:基于 Dapper 扩展你的数据访问方法
  11. python学习——读取染色体长度(一、简化问题)
  12. .net core Ocelot实现API网关并部署在docker中
  13. centos7下安装docker(16.1docker跨主机存储--Rex-Ray)
  14. AngularJS:directive自定义的指令
  15. Java集合源码学习(二)ArrayList
  16. Testing - 软件测试知识梳理 - 相关词汇
  17. 树莓派集群实践——nfs
  18. Python正则表达式学习记录
  19. 转载文章CSS3的calc()使用
  20. TMS320VC5509片内ADC采集

热门文章

  1. 腾讯工程师教你玩转 RocksDB
  2. Spring中Quartz的配置及corn表达式
  3. 文件A包含文件B,找出A不包含B的那部分
  4. MathUtils
  5. Ajax发送数据
  6. oralce plsql案例练习
  7. vue打包后不使用服务器直接访问方法
  8. BZOJ 3209: 花神的数论题 [数位DP]
  9. HDU 3595 GG and MM [Every-SG]
  10. BZOJ 1194: [HNOI2006]潘多拉的盒子 [DP DFA]