题意

给定一个长度为 \(n\) 的 \(01\) 串,完成 \(m\) 种操作——操作分两种翻转 \([l,r]\) 区间中的元素、求区间 \([l,r]\) 有多少个不同的子序列。

\(1 \leq n,m \leq 10^5\)

思路

看到这种题目,应该条件反射的去想一下线段树。

但首先还是从一个询问开始,对于一个长度为 \(n\) 的串,设 \(dp_{i,j}\) 为前 \(i\) 位组成的序列中,以 \(j\) 结尾的串的个数,若串的第 \(i\) 位为 \(j\) 有递推式:

\(dp_{i,j}=dp_{i-1,0}+dp_{i-1,1}+1\)

\(dp_{i,!j}=dp_{i-1,!j}\)

上式是以 \(0j,1j\) 结尾的串的个数,加上单独一个\(j\) ;下式则直接转移上一位的信息。

那么将 \(\{dp_{0,0},dp_{0,1},1\}\) 作为初始矩阵,用线段树维护区间对应的转移矩阵即可。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+5;
const int P=1e9+7;
struct Matrix
{
int n,m,a[4][4];
int *operator [](const int x){return a[x];}
void resize(int _n,int _m){n=_n,m=_m;}
Matrix operator *(const Matrix &_)const
{
Matrix res;res.resize(n,_.m);
FOR(i,1,n)FOR(j,1,_.m)
{
res[i][j]=0;
FOR(k,1,m)(res[i][j]+=1ll*a[i][k]*_.a[k][j]%P)%=P;
}
return res;
}
void flip()
{
swap(a[1][1],a[2][2]);
swap(a[1][2],a[2][1]);
swap(a[3][1],a[3][2]);
}
Matrix operator *=(const Matrix &_){return (*this)=(*this)*_;}
};
const Matrix Zero=(Matrix){
3,3,
0,0,0,0,
0,1,0,0,
0,1,1,0,
0,1,0,1};
const Matrix One =(Matrix){
3,3,
0,0,0,0,
0,1,1,0,
0,0,1,0,
0,0,1,1};
Matrix nd[N<<2],A;
int tag[N<<2];
char str[N]; void build(int k,int l,int r)
{
tag[k]=0;
if(l==r)
{
if(str[l]=='0')nd[k]=Zero;
else nd[k]=One;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
nd[k]=nd[k<<1]*nd[k<<1|1];
}
void push_down(int k)
{
if(!tag[k])return;
tag[k<<1]^=1,nd[k<<1].flip();
tag[k<<1|1]^=1,nd[k<<1|1].flip();
tag[k]=0;
}
void update(int k,int L,int R,int l,int r)
{
if(L<=l&&r<=R)
{
tag[k]^=1,nd[k].flip();
return;
}
push_down(k);
int mid=(l+r)>>1;
if(L<=mid)update(k<<1,L,R,l,mid);
if(R>mid)update(k<<1|1,L,R,mid+1,r);
nd[k]=nd[k<<1]*nd[k<<1|1];
}
Matrix query(int k,int L,int R,int l,int r)
{
if(L<=l&&r<=R)return nd[k];
push_down(k);
int mid=(l+r)>>1;
if(R<=mid)return query(k<<1,L,R,l,mid);
else if(L>mid)return query(k<<1|1,L,R,mid+1,r);
else return query(k<<1,L,R,l,mid)*query(k<<1|1,L,R,mid+1,r);
} int main()
{
A.resize(1,3);
A[1][1]=0,A[1][2]=0,A[1][3]=1;
int T,n,Q;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&Q);
scanf("%s",str+1);
build(1,1,n);
int op,x,y;
while(Q--)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1)update(1,x,y,1,n);
else
{
Matrix res=A*query(1,x,y,1,n);
printf("%d\n",(res[1][1]+res[1][2])%P);
}
}
}
return 0;
}

HDU 6155 Subsequence Count(矩阵乘法+线段树+基础DP)的更多相关文章

  1. 2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6155 Subsequence Count 矩阵快速幂

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6155 题意: 题解来自:http://www.cnblogs.com/iRedBean/p/73982 ...

  2. HDU 3074.Multiply game-区间乘法-线段树(单点更新、区间查询),上推标记取模

    Multiply game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  3. HDU 6155 Subsequence Count 线段树维护矩阵

    Subsequence Count Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Oth ...

  4. 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] ...

  5. HDU 6155 Subsequence Count (DP、线性代数、线段树)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6155 题解 DP+线代好题.(考场上过多时间刚前两题,没怎么想这题--) 首先列出一个DP式: 设\( ...

  6. THUSCH 2017 大魔法师(矩阵乘法+线段树)

    题意 https://loj.ac/problem/2980 思路 区间修改考虑用线段树维护.由于一段区间的 \(A,B,C\) 可以表示成由原来的 \(A,B,C\) 乘上带上系数再加上某一个某个常 ...

  7. Luogu P4643 【模板】动态dp(矩阵乘法,线段树,树链剖分)

    题面 给定一棵 \(n\) 个点的树,点带点权. 有 \(m\) 次操作,每次操作给定 \(x,y\) ,表示修改点 \(x\) 的权值为 \(y\) . 你需要在每次操作之后求出这棵树的最大权独立集 ...

  8. ZOJ 2671 Cryptography 矩阵乘法+线段树

    B - Cryptography Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu Subm ...

  9. hdu 6155 - Subsequence Count

    话说这题比赛时候过的好少,连题都没读TOT 先考虑dp求01串的不同子序列的个数. dp[i][j]表示用前i个字符组成的以j为结尾的01串个数. 如果第i个字符为0,则dp[i][0] = dp[i ...

随机推荐

  1. jQuery选择器--selector1,selector2,selectorN和ancestor descendant

        selector1,selector2,selectorN 概述 将每一个选择器匹配到的元素合并后一起返回.你可以指定任意多个选择器,并将匹配到的元素合并到一个结果内 参数 selector1 ...

  2. c# 继承与多种状态

    可访问性: public                      无限制 internal                   只允许在同一个程序集访问 protected              ...

  3. DOM jquery

    DOM  文档对象模型(Document Object Model)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.我们最为关心的是,DOM ...

  4. 源码下载:74个Android开发开源项目汇总

    1. ActionBarSherlock ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便地使用所 ...

  5. [转载]ViewPort <meta>标记

    ViewPort <meta>标记用于指定用户是否可以缩放Web页面,如果可以,那么缩放到的最大和最小缩放比例是什么.使用 ViewPort <meta>标记还表示文档针对移动 ...

  6. jquery操作节点

    var v= $("input[type='checkbox'][name='ids']:checked").closest('tr').find('td:eq(2)').map( ...

  7. flask框架----基于flask的扩展实现的简单的页面登录

    废话不多说,直接上代码 from flask import Flask,render_template,request,redirect,session app = Flask(__name__,te ...

  8. Powerpoint 演示时定时提醒工具

    经常碰到这样的场景,规定的演讲报告时间所剩无几,甚至是已经超时,但演讲者并不知情,做为主持人只能从旁边轻轻的善意的提醒,但有时演讲者会没注意到主持人的提醒... 这里要介绍的就是这样一款用于提醒演讲者 ...

  9. 计算概论(A)/基础编程练习1(8题)/2:苹果和虫子

    #include<stdio.h> #include<math.h> int main() { /* n个苹果 每x小时能吃掉一个苹果 经过y小时 */ float n, x, ...

  10. Java线程安全容器

    一.Java同步容器 同步容器是用来解决并发情况下的容器线程安全问题的.给多线程环境准备一个线程安全的容器对象. 线程安全的容器对象: Vector, Hashtable.线程安全容器对象,都是使用s ...