hdu6155

该博客讨论了CCPC网络赛中的一个难题,涉及01字符串和两种操作:字符区间翻转及查询不同子序列数量。通过动态规划 dp[i][0/1] 分析以0或1结尾的子序列,利用矩阵乘法的结合律转化为区间问题,并借助线段树维护区间矩阵积。在区间翻转时,通过交换翻转和未翻转矩阵积实现更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Subsequence Count

题目链接
ccpc网络赛1006
题意是给一个01字符串,然后有2种操作,
1、把l到r这个区间的字符翻转,
2、查询l到r这个区间有多少个不同的子序列,(注意是子序列,可不连续),对1e9+7取模
这个题目在比赛的时候过的人很少
其实对于第二种操作,我们容易得到一个dp[i][0/1]代表前i个字符以0/1结尾的不同子序列有多少个,容易得到方程

if(s[i] == 0)dp[i][0] =dp[i-1][0] + dp[i-1][1] + 1 

S[i] == 1同理,然后就是转化区间上的问题,容易想到用一些数据结构来维护,比如线段树,但是用线段树的话必须把这个递推的方程转化为一个可以区间合并的问题。我们知道dp转移有时候可以变为乘上一个矩阵,而且矩阵是满足结合律的,也就是说可以用来区间合并。
当S[i] 为1时,我们可以构造出一个矩阵A,S[i]为0时同理,可以构造出矩阵B
然后我们就可以用线段树来维护一个区间的矩阵的积,
在修改的时候,我们要把某个区间的A矩阵变为B,B矩阵变为A。我们可以对于一个节点可以记录一段区间翻转和未翻转的矩阵积,当某一个区间需要被翻转的时候,把未翻转矩阵和翻转矩阵交换一下即可。因为有Push_up的存在,保证了节点里矩阵是正确的。

#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const LONG  INF=0x3f3f3f3f;
const LONG  MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)|1
#define root 1, n , 1
const int MAXN  = 1e5 +100 ;
struct Matr
{
    LONG  matr[3][3] ;
    void Init()
    {
        clr0(matr) ;
    }
};
Matr x ,y ;
struct Tree
{
  Matr Real ;
  Matr Temp ;
} tree[MAXN<<2] ;
int lazy [MAXN<<2] ;
char S[100100] ;
Matr Multi(Matr a , Matr b)
{
    Matr ret ;
    ret.Init() ;
    for(int i = 0;i < 3 ; ++i)
        for(int j = 0 ;j<  2 ;  ++ j)
            for(int k=0;k< 3; ++k)
                ret.matr[i][j] = ( ret.matr[i][j] + a.matr[i][k] * b.matr[k][j] ) %MOD ;
    ret.matr[2][2]= 1;
    return ret;
}
void Push_up(int rt )
{
    tree[rt].Real = Multi(tree[rt<<1].Real , tree[rt<<1|1].Real) ;
    tree[rt].Temp = Multi(tree[rt<<1].Temp , tree[rt<<1|1].Temp ) ;
}
void Build(int l , int r , int rt)
{
    lazy[rt] = 0;
    if(l == r )
    {
        if(S[l] == '0')
        {
            tree[rt].Real = y ;
            tree[rt].Temp = x ;
        }
        else
        {
            tree[rt].Real = x ;
            tree[rt].Temp = y ;
        }
        return ;
    }
    int mid = (l + r ) /2 ;
    Build(lson ) ;
    Build (rson ) ;
    Push_up(rt) ;
}
void Push_down(int rt )
{
    if(lazy[rt])
    {
        Matr tmp = tree[rt<<1].Real ;
        tree[rt<<1].Real = tree[rt<<1].Temp ;
        tree[rt<<1].Temp = tmp ;
        tmp = tree[rt<<1|1 ].Real ;
        tree[rt<<1|1].Real = tree[rt<<1|1].Temp ;
        tree[rt<<1|1].Temp = tmp ;
        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;
        Matr tmp = tree[rt].Real ;
        tree[rt].Real = tree[rt].Temp ;
        tree[rt].Temp = tmp ;
        return ;
    }
    Push_down(rt) ;
    int mid = (l + r) / 2;
    if(L <= mid)
        Update(L, R , l, mid , rt<<1 ) ;
    if(R > mid )
            Update(L,R , mid + 1 , r , rt<<1|1) ;
    Push_up(rt) ;

}
Matr Que(int L ,int R ,int l , int r ,int rt )
{
    if(L <= l &&r <= R )
    {
        return tree[rt].Real ;
    }
    Push_down(rt) ;
    int mid = (l +r) / 2;
    Matr res ;
    res.Init() ;
    res.matr[0][0] =1 , res.matr[2][2] = 1 , res.matr[1][1] = 1;
    if(L <=mid )
    res = Multi( res ,Que(L , R , lson ) ) ;
    if(R > mid)
        res = Multi(res , Que(L ,R ,rson ) ) ;
    return res ;
}
int main()
{
    int T ;
    cin >>T ;
    x.Init() ;
    y.matr[0][0] = 1 ,y.matr[1][0] = 1,y.matr[2][0] = 1 ,y.matr[0][1] = 0,y.matr[1][1] = 1 ,y.matr[2][1] = 0,y.matr[0][2] = 0 ,y.matr[1][2] = 0, y.matr[2][2] = 1;
    x.matr[0][0] = 1 ,x.matr[1][0] = 0,x.matr[2][0] = 0 ,x.matr[0][1] = 1,x.matr[1][1] = 1 ,x.matr[2][1] = 1,x.matr[0][2] = 0 ,x.matr[1][2] = 0, x.matr[2][2] = 1;
    int n , Q ;
    while(T --)
    {
        scanf("%d%d",&n,&Q) ;
        scanf("%s",S+1) ;
        Build( root )  ;
        while(Q -- )
        {
            int op ;
            scanf("%d",&op) ;
            int l, r ;
            if(op == 1)
            {
                 scanf("%d%d",&l,&r) ;
                 Update(l ,r ,root ) ;
            }
            else
            {
                scanf("%d%d",&l , &r) ;
                Matr res ;
                res = Que(l , r ,root ) ;
                printf("%lld\n",(res.matr[2][0] + res.matr[2][1] ) % MOD ) ;
            }
        }
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值