HDU 6155 Subsequence Count(dp+线段树)

202 篇文章 1 订阅
59 篇文章 0 订阅

Description

给出一个长度为 n 01 s ,两种操作:

1.反转区间[l,r],即把 0 变成1,把 1 变成0

2.询问区间 [l,r] 中不同的子序列个数

Input

第一行输入一整数 T 表示用例组数,之后输入两个整数n,q分别表示串长和操作数,之后输入一个长度为 n 01串,最后 q 行每行一种操作(1T5,1n,q105)

Output

对于每个 2 操作,输出不同子序列个数,结果模109+7

Sample Input

2
4 4
1010
2 1 4
2 2 4
1 2 3
2 1 4
4 4
0000
1 1 2
1 2 3
1 3 4
2 1 4

Sample Output

11
6
8
10

Solution

先不考虑反转操作,对于串 s1s2...sn ,以 dp[i][0/1] 表示以第 i 位结尾的不同的子序列个数

如果si=0,以第 i 位结尾的子序列有三种,只有第i位的 0 ,末尾0超过 1 个,末尾0只有一个,故有转移 dp[i][0]=dp[i1][0]+dp[i1][1]+1,dp[i][1]=dp[i1][1]

如果 si=1 ,同理可得 dp[i][0]=dp[i1][0],dp[i][1]=dp[i1][0]+dp[i1][1]+1

A=111010001 B=100111001

则如果 si=0 ,则有 (dp[i][0]  dp[i][1]  1)=(dp[i1][0]  dp[i1][1]  1)A

如果 si=1 ,则有 (dp[i][0]  dp[i][1]  1)=(dp[i1][0]  dp[i1][1]  1)B

而注意到 A B只差交换前两行和前两列,令 P=010100001 为初等矩阵,注意到 P1=P ,故有 A=PBP,B=PAP

现在考虑反转操作,对于区间 [l,r] ,每个位置对应一个转移矩阵 (A/B) ,这些转移矩阵的乘积即为这个区间的转移矩阵,令这些转移矩阵为 Cl,...,Cr ,反转后转移矩阵为 Dl,...,Dr ,则有 Di=PCiP ,进而 DlDl+1...Dr=PClCl+1...CrP ,即反转后的区间转移矩阵即为反转前区间转移矩阵交换前两行和前两列,用线段树维护区间转移矩阵的乘积即可,每次修改不需要把该区间转移矩阵重新乘一遍,只需将之前的转移矩阵交换前两行前两列即可,这里一个小优化是转移矩阵前两行最后一个数字均为 0 ,不需要交换,这样只需进行5次交换即可实现转移矩阵交换前两行前两列的操作

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 100005
#define mod 1000000007
#define ls (t<<1)
#define rs ((t<<1)|1)
typedef int Mat[3][3];
int T,n,q;
char s[maxn]; 
Mat m[maxn<<2],A[2]={{1,0,0,1,1,0,1,0,1},{1,1,0,0,1,0,0,1,1}},ans;
bool Lazy[maxn<<2];
void mul(Mat a,Mat b,Mat &c)
{
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
        {
            ll temp=0;
            for(int k=0;k<3;k++)temp+=(ll)a[i][k]*b[k][j];
            c[i][j]=temp%mod;
        }
}
void deal(int t)
{
    swap(m[t][0][0],m[t][0][1]);
    swap(m[t][1][0],m[t][1][1]);
    swap(m[t][2][0],m[t][2][1]);
    swap(m[t][0][0],m[t][1][0]);
    swap(m[t][0][1],m[t][1][1]);
}
void build(int l,int r,int t)
{
    Lazy[t]=0;
    if(l==r)
    {
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                m[t][i][j]=A[s[l]-'0'][i][j];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,ls);build(mid+1,r,rs);
    mul(m[ls],m[rs],m[t]);
}
void push_down(int t)
{
    if(Lazy[t])
    {
        Lazy[t]=0;
        Lazy[ls]^=1,Lazy[rs]^=1;
        deal(ls),deal(rs);
    }
}
void update(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)
    {
        Lazy[t]^=1;
        deal(t);
        return ;
    }
    push_down(t);
    int mid=(l+r)/2;
    if(L<=mid)update(L,R,l,mid,ls);
    if(R>mid)update(L,R,mid+1,r,rs);
    mul(m[ls],m[rs],m[t]);
}
void query(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)
    {
        Mat temp;
        mul(ans,m[t],temp);
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                ans[i][j]=temp[i][j];
        return ;
    }
    push_down(t);
    int mid=(l+r)/2;
    if(L<=mid)query(L,R,l,mid,ls);
    if(R>mid)query(L,R,mid+1,r,rs);
    mul(m[ls],m[rs],m[t]);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        scanf("%s",s+1);
        build(1,n,1);
        while(q--)
        {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            if(op==1)update(l,r,1,n,1);
            else
            {
                memset(ans,0,sizeof(ans));
                ans[0][0]=ans[1][1]=ans[2][2]=1;
                query(l,r,1,n,1);
                printf("%d\n",(ans[2][0]+ans[2][1])%mod);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值