hdu 6155 - 线段树+矩阵

题目链接:点击打开链接

 

题解思路:假设dp[i][1]为到i位置尾部是1个个数有几个,dp[i][0]也是如此,那么假设第i个位置放1,那么dp[i][1] = dp[i-1][1]+dp[i-1][0]+1,dp[i][0] = dp[i-1][0],如果第i个位置放0那么dp[i][0] = dp[i-1][1]+dp[i-1][0]+1,dp[i][1] = dp[i-1][1].我们不妨用矩阵来解决这个问题,那么放1的状态转移矩阵应该是

1 1 1    放0的是 1 0 0

0 1 0                   1 1 1

0 0 1,               0 0 1,这里也要注意线段树矩阵合并要右边乘以左边,因为矩阵不满足交换律。还有可以把3*3*3的矩阵乘法优化成2*2*3,毕竟最后一行都是相同的且前两个肯定都是零。

 

代码:

 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
const int mx = 1e5+10,mod = 1e9+7;
struct data{
    ll marx[3][3];
}ma[mx<<2][2];
const data da[2] = {{1,0,0,1,1,1,0,0,1},{1,1,1,0,1,0,0,0,1}};
bool lazy[mx<<2];
char str[mx];
ll ans1,ans2;
int n,m;
void push_up(int rt){
    int rt1 = rt<<1,rt2 = rt1|1;
    int t1 = lazy[rt1],t2 = lazy[rt2],n1 = t1^1,n2 = t2^1;
    for(int i=0;i<2;i++){
        for(int j=0;j<3;j++){
            ll c=0;
            for(int k=0;k<2;k++)
            c = (c+ma[rt2][t2].marx[i][k]*ma[rt1][t1].marx[k][j]%mod)%mod;
            ma[rt][0].marx[i][j] =c;
        }
        ma[rt][0].marx[i][2] = (ma[rt][0].marx[i][2] + ma[rt2][t2].marx[i][2])%mod;
    }
    for(int i=0;i<2;i++){
        for(int j=0;j<3;j++){
            ll c=0;
            for(int k=0;k<2;k++)
            c = (c+ma[rt2][n2].marx[i][k]*ma[rt1][n1].marx[k][j])%mod;
            ma[rt][1].marx[i][j] =c;
        }
        ma[rt][1].marx[i][2] = (ma[rt][1].marx[i][2] + ma[rt2][n2].marx[i][2])%mod;
    }
}
void build(int l,int r,int rt){
    lazy[rt] = 0;
    ma[rt][0].marx[2][2] = ma[rt][1].marx[2][2] = 1;
    if(r==l){
        int c = str[r]-'0';
        ma[rt][0] = da[c],ma[rt][1] = da[c^1];
        return ;
    }
    int mid = (l+r)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}
void push_down(int rt){
    if(lazy[rt]){
        lazy[rt<<1] ^= 1;
        lazy[rt<<1|1] ^= 1;
        lazy[rt] = 0;
    }
}
void update(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r) { lazy[rt] ^= 1; return ; }
    push_down(rt);
    int mid = (l+r)>>1;
    if(mid>=L) update(L,R,lson);
    if(mid<R) update(L,R,rson);
    push_up(rt);
}
void query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        ll nx,ny;
        bool ts = lazy[rt];
        nx = ma[rt][ts].marx[0][0]*ans1%mod;
        nx = (nx+ma[rt][ts].marx[0][1]*ans2+ma[rt][ts].marx[0][2])%mod;
        ny = ma[rt][ts].marx[1][0]*ans1%mod;
        ny = (ny+ma[rt][ts].marx[1][1]*ans2+ma[rt][ts].marx[1][2])%mod;
        ans1 = nx,ans2 = ny;
        return ;
    }
    int mid = (l+r)>>1;
    push_down(rt);
    if(mid>=L) query(L,R,lson);
    if(mid<R) query(L,R,rson);
    push_up(rt);
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        scanf("%s",str+1);
        build(1,n,1);
        while(m--){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(x&1) update(y,z,1,n,1);
            else{
                ans1 = ans2 = 0;
                query(y,z,1,n,1);
                printf("%lld\n",(ans1+ans2)%mod);
            }
        }
    }
    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值