题目描述:
给你一串由01组成的串
操作1:将[l,r]中的数取反,即0变为1,1变为0.
操作2:查询从[l,r]这个区间中本质不同的子串个数.
题目分析:
首先通过DP得到答案
DP[i][0]表示i位置结尾为0的本质不同的子串个数
DP[i][1]表示i位置结尾为1的本质不同的子串个数
当val[i]==0时
DP[i][0]=DP[i-1][0]+DP[i-1][1]+1 DP[i][1]=DP[i-1][1]
反之
DP[i][0]=DP[i-1][0] DP[i][1]=DP[i-1][0]+DP[i-1][1]+1
用矩阵表示转移即为
为什么要用矩阵表示?!
对于[f(i-1,0) f(i-1,1) 1]这个矩阵
同样可以 由[f(i-2,0) f(i-2,1) 1]这个矩阵乘一个矩阵转移而来
于是[f(i,0) f(i,1) 1]=[0 0 1] * [???] * [???] * … *[???]
我们可以把后面的矩阵结合起来(矩阵乘法符合结合律)
于是我们可以用线段树维护这些矩阵.
即0的节点为第一个矩阵,1的节点为第二个矩阵.
方法即为:线段树套矩阵!
当然还有一个问题.
那就是我们还有序列取反的问题
我们同样可以使用一个懒标记!
通过观察我们发现 第一个矩阵 经过 第一行和第二行的交换,第一列和第二列的交换即为第二个矩阵!!!
矩阵乘法满足一个规律,即两个子矩阵分别经过
第一行和第二行的交换,第一列和第二列的交换 这个过程
然后相乘得出的矩阵
即为原相乘矩阵经过相同操作后得到的矩阵,那么就可以引入懒标记了.
Ac 代码:
鄙人没有用结构体进行封装,用的函数较多 qwq
另外吐槽一句,为啥不开long long一个点都不对!
#include <cstdio>
#include <iostream>
#include <cstring>
#define il inline
#define T tree[o]
#define swap std::swap
#define ll long long
const int maxm=1e5+1;
const int mod=1e9+7;
ll ans[4][4],c[4][4];
ll x[1][3],y[1][3];
ll val[maxm];
struct Matrix{
ll a[4][4];
int flag;
}tree[maxm*4];
il void new_matrix(int o,int l,int r)
{
if(val[l]==0)
T.a[1][1]=1,T.a[2][1]=1,T.a[2][2]=1,T.a[3][1]=1,T.a[3][3]=1;
else
T.a[1][1]=1,T.a[1][2]=1,T.a[2][2]=1,T.a[3][2]=1,T.a[3][3]=1;
}//根据值进行矩阵初始化
il void change(int o)
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
ans[i][j]=T.a[i][j];
}
int flagx=1;//全局标记
il void get_matrix(int o,int l,int r)
{
if(l>=r) return;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
T.a[i][j]=0;//初始化矩阵
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++)
{
T.a[i][j]=(T.a[i][j]+(tree[(o<<1)].a[i][k]%mod*tree[(o<<1)|1].a[k][j]%mod)%mod)%mod;//儿子矩阵乘法
}
}
il void cfx(int o)
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
c[i][j]=ans[i][j]%mod,ans[i][j]=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++)
ans[i][j]=(ans[i][j]+(c[i][k]%mod*tree[o].a[k][j]%mod)%mod)%mod;
}
void build(int o,int l,int r)//建线段树
{
std::memset(T.a,0,sizeof(T.a));
if(l>=r) new_matrix(o,l,r);
else
{
int mid=(l+r)>>1;
build((o<<1),l,mid);
build((o<<1)|1,mid+1,r);
}
get_matrix(o,l,r);
}
il void update(int o)//进行交换操作
{
if(tree[o].flag==1) tree[o].flag=0;//两次取反为不做
else tree[o].flag=1;
swap(T.a[1][1],T.a[2][1]),swap(T.a[1][2],T.a[2][2]),swap(T.a[1][3],T.a[2][3]);
swap(T.a[1][1],T.a[1][2]),swap(T.a[2][1],T.a[2][2]),swap(T.a[3][1],T.a[3][2]);
}
il void pushdown(int o)//标记下放
{
if(!tree[o].flag) return;
tree[o].flag=0;
update((o<<1)),update((o<<1)|1);
}
void modify(int o,int l,int r,int ql,int qr)//取反操作
{
if(r<ql||l>qr) return;
if(l>=ql&&r<=qr)
{
update(o);
return;
}
pushdown(o);
int mid=(l+r)>>1;
modify((o<<1),l,mid,ql,qr);
modify((o<<1)+1,mid+1,r,ql,qr);
get_matrix(o,l,r);
}
void query(int o,int l,int r,int ql,int qr)
{
if(r<ql||l>qr) return;
if(l>=ql&&r<=qr)
{
if(flagx) change(o),flagx=0;
else cfx(o);
return;
}
pushdown(o);
int mid=(l+r)>>1;
query((o<<1),l,mid,ql,qr);
query((o<<1)|1,mid+1,r,ql,qr);
}
il ll Ans(int l,int r,int n)
{
flagx=1;
x[1][1]=y[1][1]=0,x[1][2]=y[1][2]=0,x[1][3]=1,y[1][3]=0;
query(1,1,n,l,r);
for(int i=1;i<=1;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++)
y[i][j]=(y[i][j]+(x[i][k]%mod*ans[k][j]%mod)%mod)%mod;
return (y[1][1]+y[1][2])%mod;//最后 f(i,0)+f(i,1)即为答案
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&val[i]);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int opt,l,r;
scanf("%d%d%d",&opt,&l,&r);
//printf("%d %d %d %d %d\n",opt,l,r,i,m);
if(opt==1) modify(1,1,n,l,r);
else printf("%lld\n",Ans(l,r,n));
}
return 0;
}