ccpc 网络赛 hdu 6155

# ccpc 网络赛 hdu 6155(矩阵乘法 + 线段树)

题意:

给出 01 串,要么询问某个区间内不同的 01 子序列数量,要么把区间翻转。

叉姐的题解:

先考虑怎么算 \(s_1, s_2, \ldots, s_n\)的答案。
\(dp(i, 0/1)\) 表示考虑到 \(s_i\)
,以 \(0/1\) 结尾的串的数量。
那么 \(dp(i, 0) =dp(i - 1, 0) + dp(i - 1, 1) + 1\)\(1\)也同理。
那么假设在某个区间之前,\(dp(i, 0/1) = (x, y)\) 的话,过了这段区间,就会变成 \((ax + by + c, dx + ey + f)(ax+by+c,dx+ey+f)\) 的形式,只要用线段树维护这个线性变化就好了。


fzu 2129 子序列的个数 做过这道题的话 dp方程应该写的出来

一直在思考如何区间合并这个东西,或者区间是否具有加减性质,可以的话就可以用线段树做了,然而并不能

维护矩阵真的是涨姿势了
先列出转移矩阵把

如果\(s_i = 0\),则有
\(dp[i][0] = 1 * dp[i-1][0] + 1 * dp[i-1][1] + 1 * 1\)
\(dp[i][1] = 0 * dp[i-1][0] + 1 * dp[i-1][1] + 0 * 1\)
否则
\(dp[i][0] = 1 * dp[i-1][0] + 0 * dp[i-1][1] + 0 * 1\)
\(dp[i][1] = 1 * dp[i-1][0] + 1 * dp[i-1][1] + 1 * 1\)

初始化\(dp[0][0/1] = 0\)
初始矩阵就为
\(\begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}\)
如果当前为0,矩阵是这样的
\(\begin{bmatrix} 1& 1 & 1\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix}\)
否则就是这样的
\(\begin{bmatrix} 1& 0 & 0\\ 0& 1 & 1\\ 0& 0 & 1 \end{bmatrix}\)

假设当前字符串为01,那么最后矩阵就是
\(\begin{bmatrix} 1& 0 & 0\\ 0& 1 & 1\\ 0& 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 1 & 1\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix} = \begin{bmatrix} 1\\ 2\\ 1 \end{bmatrix}\)

通过1和0的转移矩阵容易知道区间翻转其实就是把矩阵的某些元素交换一下

然后写个线段树区间合并就好了

#include<bits/stdc++.h>
#define LL long long
#define ls rt<<1
#define rs (rt<<1|1)
using namespace std;
void read(int &x){
    char c = getchar();
    x = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
}
const int mod = 1e9 + 7;
const int N = 1e5 + 10;

struct MAT{
    int a[3][3];
    void change(){
        swap(a[0][2],a[1][2]);
        swap(a[0][0],a[1][1]);
        swap(a[0][1],a[1][0]);
    }
}s[N << 2];
void add(int &x,int y){
    x += y;
    if(x >= mod) x -= mod;
}
MAT mul(MAT A,MAT B){
  MAT ans;
  for(int i = 0;i < 3;i++){
    for(int j = 0;j < 3;j++){
        ans.a[i][j] = 0;
        for(int k = 0;k < 3;k++){
            add(ans.a[i][j],1LL * A.a[i][k] * B.a[k][j]%mod);
        }
    }
  }
  return ans;
}
MAT mat[3];
int col[N << 2];
char S[N];
void init(){
    for(int k = 0;k < 3;k++)
        for(int i = 0;i < 3;i++)
            for(int j = 0;j < 3;j++)
                mat[k].a[i][j] = i == j?1:0;
    mat[1].a[0][1] = mat[1].a[0][2] = 1;
    mat[2].a[1][0] = mat[2].a[1][2] = 1;
}
void pushdown(int rt){
    if(col[rt]){
        col[rs] ^= 1;
        col[ls] ^= 1;
        s[rs].change();
        s[ls].change();
        col[rt] = 0;
    }
}
void flip(int L,int R,int l,int r,int rt){
    if(L <= l && R >= r){
        col[rt] ^= 1;
        s[rt].change();
        return ;
    }
    pushdown(rt);
    int m = l + r >> 1;
    if(L <= m) flip(L,R,l,m,ls);
    if(R > m) flip(L,R,m+1,r,rs);
    s[rt] = mul(s[rs], s[ls]);
}
MAT query(int L,int R,int l,int r,int rt){
    if(L <= l && R >= r) return s[rt];
    pushdown(rt);
    int m = l + r >> 1;
    if(L <= m && R > m) return mul(query(L,R,m+1,r,rs),query(L,R,l,m,ls));
    if(L <= m) return query(L,R,l,m,ls);
    if(R > m) return query(L,R,m+1,r,rs);
}
void build(int l,int r,int rt){
    col[rt] = 0;
    if(l == r){
        s[rt] = mat[S[l] - '0' + 1];
        col[rt] = 0;
        return ;
    }
    int m = l + r>>1;
    build(l,m,ls);
    build(m+1,r,rs);
    s[rt] = mul(s[rs], s[ls]);
}
int main(){
    init();
    int T,o,l,r,n,q;
    read(T);
    while(T--){
        read(n),read(q);
        scanf("%s",S+1);
        build(1,n,1);
        while(q--){
            read(o),read(l),read(r);
            if(o == 1)  flip(l,r,1,n,1);
            else{
                MAT ans = query(l,r,1,n,1);
                int tmp = (ans.a[0][2]+ans.a[1][2])%mod;
                printf("%d\n",tmp);
            }
        }
    }
    return 0;
}

posted @ 2017-08-20 17:35  jiachinzhao  阅读(261)  评论(0编辑  收藏  举报