Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵

Problem Description

Given a binary string S[1,...,N] (i.e. a sequence of 0‘s and 1‘s), and Q 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 l and r (inclusive).
2. Counting the number of distinct subsequences in the substring S[l,...,r].

Input

The first line contains an integer T, denoting the number of the test cases.
For each test, the first line contains two integers N and Q.
The second line contains the string S.
Then Q lines follow, each with three integers type, l and r, denoting the queries.

1≤T≤5
1≤N,Q≤105
S[i]∈{0,1},?1≤i≤N
type∈{1,2}
1≤l≤r≤N

Output

For each query of type 2, output the answer mod (109+7) in one line.

首先考虑怎么求一个01串有多少种不同的子序列

dp[i][0]表示考虑到第i位时以0结尾的不同的子序列个数

dp[i][1]表示考虑到第i位时以1结尾的不同的子序列个数

若第i+1位为1,则有:

以01为结尾的子序列个数为dp[i][0]

以11为结尾的子序列个数为dp[i][1]

只有一个1的子序列个数为1

以0为结尾的子序列个数为dp[i][0]

以上4种情况统计了考虑到i+1处时所有的子序列

于是有 dp[i+1][1]=dp[i][0]+dp[i][1]+1

   dp[i+1][0]=dp[i][0]

            (1  1  0

(dp[i][0],dp[i][1],1)* 0  1  0   =(dp[i+1][0],dp[i+1][1],1)

             0  1  1)

记为A矩阵。

若i+1位为0同理有:    

    dp[i+1][1]=dp[i][0]

    dp[i+1][0]=dp[i][0]+dp[i][1]+1

    对应矩阵为(记为B矩阵)

          1  0  0                          

          1  1  0                          

          1  0  1                          

其次考虑优化的问题。将以上的两种转移视为矩阵,用线段树维护矩阵的乘积即可。

对于将所有0换成1,1换成0的操作而言,等价于将所有A矩阵换成B,B换成A,而A和B通过交换1,2行及1,2列可互相转换,或者说,乘以初等矩阵Fs,t  ,该矩阵的逆为自身。(记其为F)

                                                            0  1  0     

                                                            1  0  0

  

                                                          0  0  1

于是有FAFFAF……FBF……=FAAB……F,或者说,将大量0换成1,1换成0的时候只需要在乘积最外面进行一次交换1,2行与1,2列的操作。

然而。。仍然TLE..估计是被卡了常数。心塞。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int N,Q;
const long long int mo=1e9+7;
char s[5010];
int lazy[5050<<2];
struct Matrix{
    int n,m;
    long long a[3][3];
    Matrix (){clear();}
    void clear(){
        n=m=3;
        memset(a,0,sizeof(a));
    }
    Matrix operator *(const Matrix &b) const{
        Matrix tmp;
        for (int i=0;i<n;++i)
            for (int j=0;j<b.m;++j)
                for (int k=0;k<m;++k)
                    tmp.a[i][j]=(tmp.a[i][j]+a[i][k]*b.a[k][j])%mo;
        return tmp;
    }
};
Matrix A0,A1,E;
Matrix cnt[5050<<2];
inline void init()
{
    A0.a[0][0]=1,A0.a[0][1]=0,A0.a[0][2]=0;
    A0.a[1][0]=1,A0.a[1][1]=1,A0.a[1][2]=0;
    A0.a[2][0]=1,A0.a[2][1]=0,A0.a[2][2]=1;

    A1.a[0][0]=1,A1.a[0][1]=1,A1.a[0][2]=0;
    A1.a[1][0]=0,A1.a[1][1]=1,A1.a[1][2]=0;
    A1.a[2][0]=0,A1.a[2][1]=1,A1.a[2][2]=1;

    E.a[0][0]=1,E.a[0][1]=0,E.a[0][2]=0;
    E.a[1][0]=0,E.a[1][1]=1,E.a[1][2]=0;
    E.a[2][0]=0,E.a[2][1]=0,E.a[2][2]=1;
}
inline void Pushup(int rt)
{
    cnt[rt]=cnt[rt<<1]*cnt[rt<<1|1];
}
inline void build(int l,int r,int rt)
{
    if(l==r)
    {
        if(s[l-1]-‘0‘==0)
        {
            cnt[rt]=A0;
        }
        else cnt[rt]=A1;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    Pushup(rt);
}
inline void change(Matrix &X)
{
    swap(X.a[0][0],X.a[1][1]);
    swap(X.a[0][1],X.a[1][0]);
    swap(X.a[2][0],X.a[2][1]);
}

inline void pushdown(int rt)
{
    if(lazy[rt])
    {
        change(cnt[rt<<1]);
        change(cnt[rt<<1|1]);
        lazy[rt<<1]^=1;
        lazy[rt<<1|1]^=1;
        lazy[rt]=0;
    }
}
inline void update(int a,int b,int l,int r,int rt)
{
    if(l>=a&&r<=b)
    {
        change(cnt[rt]);
        lazy[rt]^=1;
        return;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    if(a<=m) update(a,b,lson);
    if(b>m) update(a,b,rson);
    Pushup(rt);
}
inline void Input()
{
    scanf("%d%d",&N,&Q);
    scanf("%s",s);
}
inline Matrix query(int a,int b,int l,int r,int rt)
{
    if(l>=a&&r<=b) return cnt[rt];
    pushdown(rt);
    Matrix t1=E,t2=E;
    int m=(r+l)>>1;
    if(a<=m) t1=query(a,b,lson);
    if(b>m) t2=query(a,b,rson);
    return t1*t2;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T,type,l,r;
    scanf("%d",&T);
    init();
    rep(t,1,T)
    {
        Input();
        build(1,N,1);
        rep(i,1,Q)
        {
            scanf("%d%d%d",&type,&l,&r);
            if(type==1)
            {
                update(l,r,1,N,1);
              //  rep(j,1,2*N) printf("i=%d dp%d=%lld\n",i,j,(cnt[j].a[2][0]+cnt[j].a[2][1])%mo);
            }
            else
            {
                Matrix tmp;
                tmp=query(l,r,1,N,1);
                printf("%lld\n",(tmp.a[2][0]+tmp.a[2][1])%mo);
            }
        }
    }
    return 0;
}

代码如下:

时间: 2024-10-24 01:46:32

Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵的相关文章

[动态dp]线段树维护转移矩阵

背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区间,直接列出整个区间的dp方程.这个是基础,动态dp无论如何还是dp(这一步是一般是重点) 2.列出转移矩阵.由于有很多修改操作,我们将数据集中在一起处理,还可以利用矩阵结合律,并且区间比较好提取,(找一段矩阵就好了),修改也方便. 3.线段树维护矩阵.对于修改,我们就是在矩阵上进行修改,对于不同的

CF718C Sasha and Array(线段树维护矩阵)

题解 (不会矩阵加速的先去学矩阵加速) 反正我想不到线段树维护矩阵.我太菜了. 我们在线段树上维护一个区间的斐波那契的列矩阵的和. 然后询问时提取每个符合题意列矩阵的答案项(不是列矩阵存了两项吗,一个是当前项,一个是用来递推的) 因为矩阵乘有结合律所以区间加这个操作就直接区间乘变换矩阵的x次方就行. 然后记得开long long 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include&

HDU 6155 Subsequence Count 线段树维护矩阵

Subsequence Count Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others) Problem Description Given a binary string S[1,...,N] (i.e. a sequence of 0's and 1's), and Q queries on the string. There are two types of querie

codeforces CF718C Sasha and Array 线段树维护矩阵

$ \Rightarrow $ 戳我进CF原题 C. Underground Lab time limit per test: 1 second memory limit per test: 256 megabytes input: standard input output: standard output The evil Bumbershoot corporation produces clones for gruesome experiments in a vast undergroun

hdu 4031 2011成都赛区网络赛A题 线段树 ***

就是不知道时间该怎么处理,想了好久,看了别人的题解发现原来是暴力,暴力也很巧妙啊,想不出来的那种  -_-! 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9

CF719E. Sasha and Array [线段树维护矩阵]

CF719E. Sasha and Array 题意: 对长度为 n 的数列进行 m 次操作, 操作为: a[l..r] 每一项都加一个常数 C, 其中 0 ≤ C ≤ 10^9 求 F[a[l]]+F[a[l+1]]+...F[a[r]] mod 1e9+7 的余数 矩阵快速幂求斐波那契 矩阵满足乘法分配律和结合律! 所以可以每个节点维护矩阵/矩阵和,区间加相当于区间乘矩阵 注意:不要把快速幂写在里面,复杂度平添一个log.把\(B^C\)算出来之后传进去就好了 #include <iostr

HDU 6155 Subsequence Count(矩阵 + DP + 线段树)题解

题意:01串,操作1:把l r区间的0变1,1变0:操作2:求出l r区间的子序列种数 思路:设DP[i][j]为到i为止以j结尾的种数,假设j为0,那么dp[i][0] = dp[i - 1][1] + dp[i -1][0] (0结尾新串) + dp[i - 1][0] (0结尾旧串) - dp[i - 1][0] (重复) + 1(0本身被重复时去除). 那么可以得到转移时的矩阵 $$ \left( \begin{matrix} dp[i - 1][0] & dp[i - 1][1] &am

大连网络赛 1006 Football Games

1 //大连网络赛 1006 2 // 吐槽:数据比较水.下面代码可以AC 3 // 但是正解好像是:排序后,前i项的和大于等于i*(i-1) 4 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define LL long long 8 typedef pair<int,int> pii; 9 const double inf = 123456789012345.0; 10 const LL MOD =100000000L

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on