HDU6155 Subsequence Count

线段树与矩阵翻转
本文介绍了一种使用线段树维护区间矩阵的方法,解决包含翻转区间01串的操作与查询问题。通过矩阵变换降低复杂度,实现高效区间翻转。

题意:找子区间的子序列,多种操作,多种查询,操作为翻转区间01串;

思路:线段树维护区间矩阵;

首先看递推方程:当是s[i]=0时dp[i][0]=dp[i-1][0]+dp[i-1][1]+1,dp[i][1]=dp[i-1][1];为一的时候同理,再次发现我们需要一个三维矩阵来更新递推式,那我们来变换一下式子,dp[i][0]+1=dp[i-1][0]+1+dp[i-1][1]+1,dp[i][1]+1=dp[i-1][1]+1;那我们发现三维矩阵变成二维的了,则复杂度便从3^3降为2^2(矩阵乘积时候),接下来又发现,s[i]=0,s[i]=1的时候递推矩阵刚好是第一行变为第二行,第一列变为第二列,如此,当一个区间内的01串翻转之后相当于区间的矩阵第一行变为第二行,第一列变为第二列,对此可能不好想到,不过可以用矩阵初等变换推出,因此,每次区间翻转只需要转换一下矩阵即可,代码算是比较优化的:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define L l , m , u << 1
#define R m + 1 , r , u << 1 | 1
typedef long long LL;
const int maxn=1e5+100;
const LL mod=1e9+7;
char s[maxn];
struct Matrix
{
    LL v[2][2];
    void init(int g)
    {
        if(g)v[0][0]=v[0][1]=v[1][1]=1,v[1][0]=0;
        else v[0][0]=v[1][0]=v[1][1]=1,v[0][1]=0;
    }
    Matrix operator *(const Matrix &m)const
    {
        Matrix c;
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            {
                c.v[i][j]=0;
                for(int k=0;k<2;k++)
                    c.v[i][j]=(c.v[i][j]+v[i][k]*m.v[k][j])%mod;
            }
            return c;
    }
    void rev()
    {
        swap(v[0][0],v[1][1]);
        swap(v[0][1],v[1][0]);
    }
};

bool lz[maxn<<2];
Matrix mul[maxn<<2];
void PushUp(int u)//把当前结点的信息更新到父结点
{
    mul[u] = mul[u<<1]*mul[u<<1|1];
}
void PushDown(int u)//把当前结点的信息更新给儿子结点
{
    if (lz[u])
    {
        lz[u<<1] ^= 1;
        lz[u<<1|1] ^= 1;
        mul[u<<1].rev();
        mul[u<<1|1].rev();
        lz[u] = 0;
    }
}
void build(int l,int r,int u)
{
    lz[u] = 0;
    if (l == r)
    {
        mul[u].init(s[l]-'0');
        return ;
    }
    int m = (l + r) >> 1;
    build(L);
    build(R);
    PushUp(u);
}
void update(int l1,int r1,int l,int r,int u)
{
    if (l1 <= l && r <= r1)
    {
        lz[u] ^= 1;
        mul[u].rev();
        return ;
    }
    PushDown(u);
    int m = (l + r) >> 1;
    if (l1 <= m)
        update(l1 , r1 , L);
    if (m < r1)
        update(l1 , r1 , R);
    PushUp(u);
}

Matrix query(int l1,int r1,int l,int r,int u)
{
    if (l1 <= l && r <= r1)
    {
        return mul[u];
    }
    PushDown(u);
    int m = (l + r) >> 1;
    Matrix res;
    res.v[0][0]=res.v[1][1]=1;
    res.v[0][1]=res.v[1][0]=0;
    if (l1<= m)
        res =res * query(l1 , r1 , L);
    if (m < r1)
        res = res * query(l1 , r1 , R);
    return res;
}


int main()
{
    //freopen("input.txt","r",stdin);
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        scanf("%s",s+1);
        build(1,n,1);
        while(m--)
        {
            int k,l,r;
            scanf("%d%d%d",&k,&l,&r);
            if(k==1)update(l,r,1,n,1);
            else
            {
               Matrix t= query(l,r,1,n,1);
               LL ans=t.v[0][0]+t.v[0][1]+t.v[1][0]+t.v[1][1]-2;
               printf("%lld\n",(ans+mod)%mod);
            }
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/MeowMeowMeow/p/7420335.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值