F

考虑暴力 dp,设计如下 dp 状态:

  • 代表考虑到第 个,第 个是妖精的方案数;

  • 代表考虑到第 个,第 个是兔子,第 个没有限制的方案数;

  • 代表考虑到第 个,第 个是兔子,第 个也是兔子的方案数。

转移若 能填 则有 ,能填 则有 ,能填 则有

最终答案是 ,因为显然终态不能带限制。

f[x-1][0]=1,f[x-1][1]=f[x-1][2]=0;
for(int i=x;i<=y;i++){
	f[i][0]=f[i][1]=f[i][2]=0;
	if(a[i]==0||a[i]==1) add(f[i][2],f[i-1][0]),add(f[i][2],f[i-1][1]),add(f[i][2],f[i-1][2]);
	if(a[i]==0||a[i]==2) add(f[i][1],f[i-1][1]),add(f[i][1],f[i-1][2]);
	if(a[i]==0||a[i]==3) add(f[i][0],f[i-1][0]),add(f[i][0],f[i-1][1]);
}
cout<<(f[y][0]+f[y][1])%mod<<endl;

然后考虑优化这个 dp,将状态转移写成矩阵的形式,那么我们有:

线段树维护矩阵乘法即可,时间复杂度 ,本题中

Mat getm(int x){
    Mat mat;
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++) mat.a[i][j]=0;
    }
    if(x==0||x==3) mat.a[0][0]=mat.a[1][0]=1;
    if(x==0||x==2) mat.a[1][1]=mat.a[2][1]=1;
    if(x==0||x==1) mat.a[0][2]=mat.a[1][2]=mat.a[2][2]=1;
    return mat;
}
struct SGT{
    node tr[maxn*4];
    void pushup(int u){
        if(tr[u].l==tr[u].r) return ;
        tr[u].x=tr[ls(u)].x*tr[rs(u)].x;
    }
    void build(int u,int l,int r,int *a){
        tr[u].l=l,tr[u].r=r;
        if(l==r) return tr[u].x=getm(a[l]),void();
        int mid=(l+r)>>1;
        build(ls(u),l,mid,a),build(rs(u),mid+1,r,a),pushup(u);
    }
    void upd(int u,int x,int k){
        if(tr[u].l==tr[u].r) return tr[u].x=getm(k),void();
        int mid=(tr[u].l+tr[u].r)>>1;
        if(x<=mid) upd(ls(u),x,k);
        else upd(rs(u),x,k);
        pushup(u);
    }
    Mat query(int u,int l,int r){
        if(ir(l,r,tr[u].l,tr[u].r)) return tr[u].x;
        else if(!ofr(l,r,tr[u].l,tr[u].r)) return query(ls(u),l,r)*query(rs(u),l,r);
        else return I;
    }
}sgt;
void solve(){
    n=read<int>(),m=read<int>();
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++) I.a[i][j]=(i==j);
    }
    for(int i=1;i<=n;i++) a[i]=read<int>();
    sgt.build(1,1,n,a);
    while(m--){
        int op=read<int>(),x=read<int>(),y=read<int>();
        if(op==1) sgt.upd(1,x,y);
        else{
            Mat A=sgt.query(1,x,y);
            // for(int i=0;i<3;i++,cout<<endl){
            //     for(int j=0;j<3;j++) cout<<A.a[i][j]<<" ";
            // }
            print((A.a[0][0]+A.a[0][1])%mod,'\n');
        }
    }
}