hdu6155

hdu6155

题意

给出一个只由 \(01\) 组成的字符串 \(s\),有两种操作:

  1. 翻转区间 \([l, r]\)
  2. 查询区间 \([l, r]\) 有多少不同的子串

分析

首先考虑怎么统计区间有多少不同的子串。
\(dp[i][0]\) 表示以 \(s[i]=0\) 结尾的字符串的个数,\(dp[i][1]\) 同理。
\(s[i]=0\),有状态转移方程:\(dp[i + 1][0] = dp[i][0] + dp[i][1] + 1\)\(dp[i+1][1]=dp[i][1]\)
\(dp[i][1]\) 同理。
那么答案就是 \(dp[len][0]+dp[len][1]\)

可以用矩阵递推:
\[ \begin{bmatrix} dp[i][0] & dp[i][1] & 1 \end{bmatrix} * \begin{bmatrix} 1 & 0 & 0\\ 1 & 1 & 0\\ 1 & 0 & 1 \end{bmatrix} = \begin{bmatrix} dp[i + 1][0] & dp[i + 1][1] & 1 \end{bmatrix} \]

又矩阵存在结合律,所以一段区间的查询,只需要求右边一系列乘数的乘积即可。
我们可以使用线段树,去区间求积。

对于翻转操作,先将第一列和第二列交换,再将第一行和第二行交换。因为 \(01\) 是相对的,只需要交换对应的值即可。

可以在线段树中标记是否需要翻转某个区间。

看完题解写完后,猛然发现,这竟是一道线段树区间更新区间求和的“模板题”。

code

#include<bits/stdc++.h>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
struct Matrix {
    ll mat[3][3];
    void init() {
        memset(mat, 0, sizeof mat);
    }
    Matrix operator *(Matrix A) {
        Matrix B;
        B.init();
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < 3; j++) {
                for(int k = 0; k < 3; k++) {
                    B.mat[i][j] += mat[i][k] * A.mat[k][j];
                }
                B.mat[i][j] %= MOD;
            }
        }
        return B;
    }
};
char s[MAXN];
int flip[MAXN << 2];
Matrix sum[MAXN << 2];
inline void magic(Matrix& A) { // 先将第一列和第二列交换,再将第一行和第二行交换
    for(int i = 0; i < 3; i++) swap(A.mat[i][0], A.mat[i][1]);
    for(int i = 0; i < 2; i++) swap(A.mat[0][i], A.mat[1][i]);
}
inline void pushUp(int rt) {
    sum[rt] = sum[rt << 1] * sum[rt << 1 | 1];
}
inline void pushDown(int rt) {
    if(flip[rt]) {
        flip[rt << 1] ^= flip[rt];
        flip[rt << 1 | 1] ^= flip[rt];
        magic(sum[rt << 1]);
        magic(sum[rt << 1 | 1]);
        flip[rt] = 0;
    }
}
void build(int l, int r, int rt) {
    flip[rt] = 0;
    if(l == r) {
        Matrix& A = sum[rt];
        if(s[l] == '0') {
            A = Matrix{1, 0, 0, 1, 1, 0, 1, 0, 1};
        } else {
            A = Matrix{1, 1, 0, 0, 1, 0, 0, 1, 1};
        }
        return;
    }
    int m = (l + r) / 2;
    build(lson);
    build(rson);
    pushUp(rt);
}
void update(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        flip[rt] ^= 1;
        magic(sum[rt]);
        return;
    }
    pushDown(rt);
    int m = (l + r) / 2;
    if(L <= m) update(L, R, lson);
    if(R > m) update(L, R, rson);
    pushUp(rt);
}
Matrix query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        return sum[rt];
    }
    pushDown(rt);
    int m = (l + r) / 2;
    Matrix A;
    A.init();
    for(int i = 0; i < 3; i++) A.mat[i][i] = 1;
    if(L <= m) A = A * query(L, R, lson);
    if(R > m) A = A * query(L, R, rson);
    pushUp(rt);
    return A;
}
//适用于正整数
template <class T>
inline void scan_d(T &ret) {
    char c; ret=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
int main() {
    int T;
    scanf("%d", &T);
    int n, q;
    while(T--) {
        scanf("%d%d", &n, &q);
        scanf("%s", s + 1);
        build(1, n, 1);
        while(q--) {
            int type, l, r;
            scan_d(type); scan_d(l); scan_d(r);
            if(type == 1) update(l, r, 1, n, 1);
            else {
                Matrix A = query(l, r, 1, n, 1);
                printf("%lld\n", (A.mat[2][0] + A.mat[2][1]) % MOD);
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/ftae/p/7414139.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值