HDU 6155 Subsequence Count dp + 矩阵+ 线段树

来源:互联网 发布:瑟尔效应 知乎 编辑:程序博客网 时间:2024/06/08 05:08

传送门:HDU6155
题意:给出一个长度为n的01串和两种操作:
1.将区间[L, R]翻转(0变1,1变0)
2.计算区间[L, R]的不同的子序列的个数
思路:一开始以为算子串数量,对着题解怼了半天没看懂。。
叉姐的题解:

来自:ICPCCamp
里面帖子的回复中有对转移方程为什么不会重复计数的解释。

然后就是如何用线段树去维护:详细讲解
但是我看了很多的博客都没有讲解为什么翻转以后的矩阵能由翻转前的矩阵行列交换得到,我只能看出来最基础的两个转移矩阵确实满足行列交换后相等的关系。。可能是我太弱了吧。。
然后我就选择了一种更加直白的方式—–线段树每个结点维护两个矩阵,分别对应翻转前后,不过这样时间复杂度和空间复杂度会稍高一点。
代码:

#include<bits/stdc++.h>#define ll long long#define inf 0x3f3f3f3f#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1using namespace std;typedef pair<int,int> P;const int MAXN = 100010;const int mod = 1e9 + 7;struct mat{    long long val[3][3];    void init()    {        memset(val, 0, sizeof val);    }    friend mat operator * (mat &a, mat &b)    {        mat c;        c.init();        for(int k = 0; k < 3; k++)        {            for(int i = 0; i < 3; i++)            {                if(a.val[i][k] == 0) continue;                for(int j = 0; j < 3; j++)                {                    c.val[i][j] = (c.val[i][j] + a.val[i][k] * b.val[k][j]) % mod;                }            }        }        return c;    }};mat a1 = {1, 0, 0, 1, 1, 0, 1, 0, 1}, b1 = {1, 1, 0, 0, 1, 0, 0, 1, 1};bool flip[MAXN << 2];struct Node{    mat a, b;}tree[MAXN << 2];char str[MAXN];void push_up(int rt){    tree[rt].a = tree[rt << 1].a * tree[rt << 1 | 1].a;    tree[rt].b = tree[rt << 1].b * tree[rt << 1 | 1].b;}void build(int l, int r, int rt){    flip[rt] = 0;    if(l == r)    {        if(str[l - 1] - '0')        {            tree[rt].a = b1;            tree[rt].b = a1;        }        else        {            tree[rt].a = a1;            tree[rt].b = b1;        }        return ;    }    int mid = (l + r) >> 1;    build(lson);    build(rson);    push_up(rt);}void push_down(int rt){    if(flip[rt])    {        swap(tree[rt << 1].a, tree[rt << 1].b);        swap(tree[rt << 1 | 1].a, tree[rt << 1 | 1].b);        flip[rt << 1] ^= 1;        flip[rt << 1 | 1] ^= 1;        flip[rt] = 0;    }}void update(int L, int R, int l, int r, int rt){    if(L <= l && r <= R)    {        flip[rt] ^= 1;        swap(tree[rt].a, tree[rt].b);        return ;    }    push_down(rt);    int mid = (l + r) >> 1;    if(L <= mid)    update(L, R, lson);    if(R > mid)    update(L, R, rson);    push_up(rt); }mat query(int L, int R, int l, int r, int rt){    if(L <= l && r <= R)    return tree[rt].a;    push_down(rt);    int mid = (l + r) >> 1;    mat res = {1, 0, 0, 0, 1, 0, 0, 0, 1};    mat x;    if(L <= mid) x = query(L, R, lson), res = res * x;    if(R > mid) x = query(L, R, rson), res = res * x;    return res;}int main(){    int T, n, q;    cin >> T;    while(T--)    {        int tp, l, r;        scanf("%d %d", &n, &q);        scanf(" %s", str);        build(1, n, 1);        while(q--)        {            scanf("%d %d %d", &tp, &l, &r);            if(tp == 1)            update(l, r, 1, n, 1);            else            {                mat ans = query(l, r, 1, n, 1);                printf("%lld\n", (ans.val[2][0] + ans.val[2][1]) % mod);            }        }    }    return 0;}

我用的转移方程来自这里。

原创粉丝点击