Description
给出一个长度为
n
的
1.反转区间
2.询问区间 [l,r] 中不同的子序列个数
Input
第一行输入一整数
T
表示用例组数,之后输入两个整数
Output
对于每个
2
操作,输出不同子序列个数,结果模
Sample Input
2
4 4
1010
2 1 4
2 2 4
1 2 3
2 1 4
4 4
0000
1 1 2
1 2 3
1 3 4
2 1 4
Sample Output
11
6
8
10
Solution
先不考虑反转操作,对于串 s1s2...sn ,以 dp[i][0/1] 表示以第 i 位结尾的不同的子序列个数
如果
如果 si=1 ,同理可得 dp[i][0]=dp[i−1][0],dp[i][1]=dp[i−1][0]+dp[i−1][1]+1
令 A=⎡⎣⎢111010001⎤⎦⎥ , B=⎡⎣⎢100111001⎤⎦⎥
则如果 si=0 ,则有 (dp[i][0] dp[i][1] 1)=(dp[i−1][0] dp[i−1][1] 1)⋅A
如果 si=1 ,则有 (dp[i][0] dp[i][1] 1)=(dp[i−1][0] dp[i−1][1] 1)⋅B
而注意到
A
和
现在考虑反转操作,对于区间
[l,r]
,每个位置对应一个转移矩阵
(A/B)
,这些转移矩阵的乘积即为这个区间的转移矩阵,令这些转移矩阵为
Cl,...,Cr
,反转后转移矩阵为
Dl,...,Dr
,则有
Di=PCiP
,进而
DlDl+1...Dr=PClCl+1...CrP
,即反转后的区间转移矩阵即为反转前区间转移矩阵交换前两行和前两列,用线段树维护区间转移矩阵的乘积即可,每次修改不需要把该区间转移矩阵重新乘一遍,只需将之前的转移矩阵交换前两行前两列即可,这里一个小优化是转移矩阵前两行最后一个数字均为
0
,不需要交换,这样只需进行
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 100005
#define mod 1000000007
#define ls (t<<1)
#define rs ((t<<1)|1)
typedef int Mat[3][3];
int T,n,q;
char s[maxn];
Mat m[maxn<<2],A[2]={{1,0,0,1,1,0,1,0,1},{1,1,0,0,1,0,0,1,1}},ans;
bool Lazy[maxn<<2];
void mul(Mat a,Mat b,Mat &c)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
ll temp=0;
for(int k=0;k<3;k++)temp+=(ll)a[i][k]*b[k][j];
c[i][j]=temp%mod;
}
}
void deal(int t)
{
swap(m[t][0][0],m[t][0][1]);
swap(m[t][1][0],m[t][1][1]);
swap(m[t][2][0],m[t][2][1]);
swap(m[t][0][0],m[t][1][0]);
swap(m[t][0][1],m[t][1][1]);
}
void build(int l,int r,int t)
{
Lazy[t]=0;
if(l==r)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
m[t][i][j]=A[s[l]-'0'][i][j];
return ;
}
int mid=(l+r)/2;
build(l,mid,ls);build(mid+1,r,rs);
mul(m[ls],m[rs],m[t]);
}
void push_down(int t)
{
if(Lazy[t])
{
Lazy[t]=0;
Lazy[ls]^=1,Lazy[rs]^=1;
deal(ls),deal(rs);
}
}
void update(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)
{
Lazy[t]^=1;
deal(t);
return ;
}
push_down(t);
int mid=(l+r)/2;
if(L<=mid)update(L,R,l,mid,ls);
if(R>mid)update(L,R,mid+1,r,rs);
mul(m[ls],m[rs],m[t]);
}
void query(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)
{
Mat temp;
mul(ans,m[t],temp);
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
ans[i][j]=temp[i][j];
return ;
}
push_down(t);
int mid=(l+r)/2;
if(L<=mid)query(L,R,l,mid,ls);
if(R>mid)query(L,R,mid+1,r,rs);
mul(m[ls],m[rs],m[t]);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
build(1,n,1);
while(q--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==1)update(l,r,1,n,1);
else
{
memset(ans,0,sizeof(ans));
ans[0][0]=ans[1][1]=ans[2][2]=1;
query(l,r,1,n,1);
printf("%d\n",(ans[2][0]+ans[2][1])%mod);
}
}
}
return 0;
}