hdu 6155(线段树+dp+矩阵)

1 篇文章 0 订阅
1 篇文章 0 订阅

Given a binary string S[1,…,N]S[1,…,N] (i.e. a sequence of 0’s and 1’s), and QQ queries on the string.

There are two types of queries:

  1. Flipping the bits (i.e., changing all 1 to 0 and 0 to 1) between ll and rr (inclusive).
  2. Counting the number of distinct subsequences in the substring S[l,…,r]S[l,…,r].
    Input
    The first line contains an integer TT, denoting the number of the test cases.

For each test, the first line contains two integers NN and QQ.

The second line contains the string SS.

Then QQ lines follow, each with three integers typetype, ll and rr, denoting the queries.

1≤T≤51≤T≤5

1≤N,Q≤1051≤N,Q≤105

S[i]∈{0,1},∀1≤i≤NS[i]∈{0,1},∀1≤i≤N

type∈{1,2}type∈{1,2}

1≤l≤r≤N1≤l≤r≤N
Output
For each query of type 2, output the answer mod (109+7109+7) in one line.
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

题意是给一个01字符串,然后有2种操作,
1、把l到r这个区间的字符翻转,
2、查询l到r这个区间有多少个不同的子序列,(注意是子序列,可不连续),对1e9+7取模

先考虑dp求01串的不同子序列的个数。

对于操作2 可以得到 dp方程
如果s[i]==0 dp[i][0]=dp[i-1][1]+dp[i-1][0]+1;
不同的子序列个数就是i-1位置 是1或者0结尾的所有的串加上这一位,那么所有的串至少长度为2,再加上它自身,那么长度为1的只有它自身

所以当 s[i]==0 dp[i][0]=dp[i-1][1]+dp[i-1][0]+1;
dp[i][1]=dp[i-1][1];

s[i]=1 dp[i][1]=dp[i-1][1]+dp[i-1][0]+1;
dp[i][0]=dp[i-1][0];

得到矩阵 这里写图片描述

那么关于序列翻转,就是相当于矩阵翻转,把A矩阵变成B矩阵就好,把序列翻转的结果,就是如果这是1矩阵 那么被矩阵相乘 要有0矩阵的效果,就是中间列和第一列互换,同时dp[i][0]和dp[i][1]结果互换。。加上翻转标记就好

#include <bits/stdc++.h>
using namespace std;
const int SIZE = 3;
const int N = 1e5+100;
const int mod = 1e9+7;
typedef long long ll;
struct Matrix
{
    ll m[SIZE][SIZE];
    friend Matrix operator* (const Matrix& a, const Matrix& b)
    {
        Matrix c;
        for (int i = 0; i < SIZE; i++)
            for (int j = 0; j < SIZE; j++)
            {
                c.m[i][j] = 0;
                for (int k = 0; k < SIZE; k++)
                    c.m[i][j] += a.m[i][k] * b.m[k][j] % mod;
                c.m[i][j] %= mod;
            }
        return c;
    }
};

int filp[N*4],a[N];
Matrix sum[N*4];

Matrix s[2]={{1,0,0,1,1,0,1,0,1},{1,1,0,0,1,0,0,1,1}};

void out(int rt)
{
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        cout<<sum[rt].m[i][j]<<" ";
        cout<<endl;
    }
}

void build(int l,int r,int rt)
{
    filp[rt]=0;
    if(l==r)
    {
        sum[rt]=s[a[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    sum[rt]=sum[rt<<1]*sum[rt<<1|1];
    // cout<<(rt<<1)<<endl;
    // out(rt<<1);
    // cout<<(rt<<1|1)<<endl;
    // out(rt<<1|1);
    // cout<<rt<<endl;
    // out(rt);
}

void huan(int rt)
{
    swap(sum[rt].m[0][0],sum[rt].m[0][1]);
    swap(sum[rt].m[1][0],sum[rt].m[1][1]);
    swap(sum[rt].m[2][0],sum[rt].m[2][1]);
    swap(sum[rt].m[0][0],sum[rt].m[1][0]);
    swap(sum[rt].m[0][1],sum[rt].m[1][1]);
}


void pushdown(int rt)
{
    if(filp[rt])
    {
        huan(rt<<1);
        huan(rt<<1|1);
        filp[rt<<1]^=1;
        filp[rt<<1|1]^=1;
        filp[rt]=0;
    }
}


void update(int l,int r,int L,int R,int rt)
{
    if(L<=l&&R>=r)
    {
        huan(rt);
        filp[rt]^=1;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(L<=mid) update(l,mid,L,R,rt<<1);
    if(R>mid) update(mid+1,r,L,R,rt<<1|1);
    sum[rt]=sum[rt<<1]*sum[rt<<1|1];
}

Matrix query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&R>=r)
    {
        return sum[rt];
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    Matrix res={1,0,0,0,1,0,0,0,1};
    if(L<=mid) res=res*query(l,mid,L,R,rt<<1);
    if(R>mid) res=res*query(mid+1,r,L,R,rt<<1|1);
    sum[rt]=sum[rt<<1]*sum[rt<<1|1];
    return res;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%1d",&a[i]);
        build(1,n,1);
        while(m--)
        {
            int op,L,R;
            scanf("%d%d%d",&op,&L,&R);
            if(op==2){
                Matrix ans=query(1,n,L,R,1);
                Matrix hh={0,0,1,0,0,0,0,0,0};
                hh=hh*ans;
                printf("%lld\n",(hh.m[0][0]+hh.m[0][1] )%mod);
            }
            else if(op==1)
                update(1,n,L,R,1);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值