Subsequence Count (线段树)
Time Limit: 1000 ms Memory Limit: 256 MB
Description
给定一个01串 $S_{1 \cdots n}$ 和 $Q$ 个操作。
操作有两种类型:
1、将 $[l, r]$ 区间的数取反(将其中的0变成1,1变成0)。
2、询问字符串 $S$ 的子串 $S_{l \cdots r}$ 有多少个不同的子序列。由于答案可能很大,请将答案对 $10^9 + 7$ 取模。
在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。
Input
第一行包含两个整数 $N$ 和 $Q$ ,分别表示字符串长度和操作次数。
第二行包含一个字符串 $S$ 。
接下来 $Q$ 行,每行3个整数 $type, l, r$ ,其中 $type$ 表示操作类型, $l, r$ 表示操作区间为 $[l, r]$ 。
Output
对于每一个 $type = 2$ 的询问,输出一个整数表示答案。
由于答案可能很大,请将答案对 $10^9 + 7$ 取模。
Sample Input |
Sample Output |
4 4 |
11 |
HINT
数据范围与约定
对于5%的数据, $N \leq 20, Q = 1$
对于10%的数据, $N \leq 1000, Q = 1$
对于20%的数据, $N \leq 10^5, Q \leq 10$
对于另外30%的数据, $1 \leq N \leq 10^5, 1 \leq Q \leq 10^5, type = 2$
对于100%的数据, $1 \leq N \leq 10^5, 1 \leq Q \leq 10^5$
题解
这道题很有意思。
首先考虑一下不带修改的解法。
设$f_{i,0}$表示$s_1...s_i$中,以$0$结尾的子序列数量;$f_{i,1}$表示$s_1...s_i$中,以$1$结尾的子序列数量。
则有方程:
若$s_i$为0:$\begin{aligned}f_{i,0}&=f_{i-1,0}+f_{i-1,1}+1\\f_{i,1}&=f_{i-1,1}\end{aligned}$
若$s_i$为1:$\begin{aligned}f_{i,0}&=f_{i-1,0}\\f_{i,1}&=f_{i-1,0}+f_{i-1,1}+1\end{aligned}$
发现这是一类线性递推,如果用一个1x3的矩阵表示原来的$f_{i,0}$与$f_{i,1}$:$\begin{pmatrix} f_0&f_1&1 \end{pmatrix}\\$(最后的1仅作为辅助计算),乘上一个3x3的转移矩阵来得到下一位的状态呢?
如果序列中这一位$s_i$为0,则在后面乘上这样一个转移矩阵$G_0$:
$$\begin{pmatrix} f_0&f_1&1\end{pmatrix}*\begin{pmatrix} 1&0&0\\1&1&0\\1&0&1 \end{pmatrix}=\begin{pmatrix} f_0+f_1+1&f_1&1\end{pmatrix}$$
如果这一位$s_i$为1,则在后面乘上另一个转移矩阵$G_1$:
$$\begin{pmatrix} f_0&f_1&1\end{pmatrix}*\begin{pmatrix} 1&1&0\\0&1&0\\0&1&1 \end{pmatrix}=\begin{pmatrix} f_0&f_0+f_1+1&1\end{pmatrix}$$
那么我们用线段树存储每一位的转移矩阵,查询时直接查询$[l,r]$的矩阵乘积,乘上初始矩阵(其实初始矩阵为$(0,0,1)$乘了相当于没乘),所以直接输出查询矩阵的$[3][1]+[3][2]$即可
处理区间数值翻转操作
最基础的想法就是,将线段树$[l,r]$叶子节点对应的转换矩阵换成另一个转换矩阵。
观察$G_0=\begin{pmatrix} 1&0&0\\1&1&0\\1&0&1 \end{pmatrix}$ 与$G_1=\begin{pmatrix} 1&1&0\\0&1&0\\0&1&1 \end{pmatrix}$
本质上,只需把第一第二行交换一下,再将第一第二列交换一下,它们都能变成对方。
第一第二行交换,相当于在$G$前乘上一个矩阵$A$。第一第二列交换,相当于在$G$后乘上这个矩阵$A$。
$$A=\begin{pmatrix} 0&1&0\\1&0&0\\0&0&1\end{pmatrix}$$
那么:
$A*G1*A=G2$ $A*G2*A=G1$
我们暂且看回原来的模型:计算一个矩阵序列。
如果要对$[l,r]$的数字翻转,假设矩阵序列是$a*b*c*d*e$,考虑如何变换:
按照我们的预想处理方式,那应该变成$(A*a*A)*(A*b*A)*(A*c*A)*(A*d*A)*(A*e*A)$。
此时我们发现,$A*A$居然是单位矩阵...
于是就变成了$A*(a*b*c*d*e)*A$。
相当于对$a*b*c*d*e$直接手动1、2行交换,1、2列交换。
回到线段树,如果要翻转,直接在对应区间维护的矩阵进行 行交换列交换,维护并下传标记即可。
神题啊!
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=,Mod=1e9+;
int n,q;
char in[N];
struct Mat{
ll a[][];
void flip(){
for(int i=;i<;i++) swap(a[i][],a[i][]);
swap(a[][],a[][]);
swap(a[][],a[][]);
}
friend Mat operator * (Mat x,Mat y){
Mat ret;
for(int i=;i<;i++)
for(int j=;j<;j++){
ret.a[i][j]=;
for(int k=;k<;k++)
ret.a[i][j]=(ret.a[i][j]+x.a[i][k]*y.a[k][j])%Mod;
}
return ret;
}
};
const Mat stand[]={{,,,,,,,,},{,,,,,,,,}};
struct SegmentTree{
int root,cnt,ch[N*][],rev[N*];
Mat info[N*];
void build(int &u,int l,int r){
if(!u) u=++cnt;
if(l==r){
info[u]=stand[in[l]==''];
return;
}
int mid=(l+r)>>;
build(ch[u][],l,mid);
build(ch[u][],mid+,r);
pushup(u);
}
void flip(int u,int l,int r,int L,int R){
if(L<=l&&r<=R){
rev[u]^=;
info[u].flip();
return;
}
pushdown(u);
int mid=(l+r)>>;
if(L<=mid) flip(ch[u][],l,mid,L,R);
if(mid<R) flip(ch[u][],mid+,r,L,R);
pushup(u);
}
Mat query(int u,int l,int r,int L,int R){
if(L<=l&&r<=R) return info[u];
pushdown(u);
int mid=(l+r)>>;
if(R<=mid) return query(ch[u][],l,mid,L,R);
if(mid<L) return query(ch[u][],mid+,r,L,R);
return query(ch[u][],l,mid,L,R)*query(ch[u][],mid+,r,L,R);
}
inline void pushup(int u){
info[u]=info[ch[u][]]*info[ch[u][]];
}
inline void pushdown(int u){
if(!rev[u]) return;
rev[ch[u][]]^=; rev[ch[u][]]^=;
info[ch[u][]].flip(); info[ch[u][]].flip();
rev[u]=;
}
}seg;
int main(){
scanf("%d%d%s",&n,&q,in+);
seg.build(seg.root,,n);
int t,l,r;
while(q--){
scanf("%d%d%d",&t,&l,&r);
if(t==)
seg.flip(seg.root,,n,l,r);
else{
Mat ans=seg.query(seg.root,,n,l,r);
printf("%lld\n",(ans.a[][]+ans.a[][])%Mod);
}
}
return ;
}
奇妙代码
Subsequence Count (线段树)的更多相关文章
- HDU 6155 Subsequence Count 线段树维护矩阵
Subsequence Count Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 256000/256000 K (Java/Oth ...
- [HDU6155]Subsequence Count(线段树+矩阵)
DP式很容易得到,发现是线性递推形式,于是可以矩阵加速.又由于是区间形式,所以用线段树维护. https://www.cnblogs.com/Miracevin/p/9124511.html 关键在于 ...
- HDU.6155.Subsequence Count(线段树 矩阵)
题目链接 首先考虑询问[1,n]怎么做 设 f[i][0/1]表示[1,i]以0/1结尾的不同子序列个数 则 \(if(A[i]) f[i][1] = f[i-1][0] + f[i-1][1] + ...
- FZU 2105 Digits Count(线段树)
Problem 2105 Digits Count Accept: 302 Submit: 1477 Time Limit: 10000 mSec Memory Limit : 262144 KB P ...
- 【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)
[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交 ...
- CSU - 1551 Longest Increasing Subsequence Again —— 线段树/树状数组 + 前缀和&后缀和
题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1551 题意: 给出一段序列, 删除其中一段连续的子序列(或者不删), 使得剩下的序列 ...
- Codeforces 750E - New Year and Old Subsequence(线段树维护矩阵乘法,板子题)
Codeforces 题目传送门 & 洛谷题目传送门 u1s1 我做这道 *2600 的动力是 wjz 出了道这个套路的题,而我连起码的思路都没有,wtcl/kk 首先考虑怎样对某个固定的串计 ...
- CF280D k-Maximum Subsequence Sum(线段树)
在做这题时我一开始把\(tag\)写入了结构体 #include <iostream> #include <cstdio> #include <cstring> # ...
- FZU 2105Digits Count(线段树 + 成段更新)
Description Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation 1: AN ...
- fzu 2105 Digits Count ( 线段树 ) from 第三届福建省大学生程序设计竞赛
http://acm.fzu.edu.cn/problem.php?pid=2105 Problem Description Given N integers A={A[0],A[1],...,A[N ...
随机推荐
- Servlet--SingleThreadModel接口,RequestDispatcher接口
SingleThreadModel接口 定义 public interface SingleThreadModel; 这是一个空接口,它指定了系统如何处理对同一个 Servlet 的调用.如果一个 S ...
- css img换行之后有空隙
这样的2个图片换行之后有空隙<img src="img/qiche.jpg" /> <br /> <img src="img/qiche.j ...
- MySQL--如何快速对比数据
在MySQL运维中,研发同事想对比下两个不同实例上的数据并找出差异,除主键外还需要对比每一个字段,如何做呢? 第一种方案,写程序将两个实例上的每一行数据取出来进行对比,理论可行,但是对比时间较长. 第 ...
- exit、_exit、abort、return的区别
转自:http://www.cnblogs.com/fixer/archive/2013/05/14/3078660.html _exit(): 跟exit功能大致相同,区别在于_exit不会清空所有 ...
- File类中的list和listFiles方法
File类中的list和listFiles方法 list()方法是返回某个目录下的所有文件和目录的文件名,返回的是String数组 listFiles()方法是返回某个目录下所有文件和目录的绝对路径, ...
- ABP官方文档翻译 9.3 NHibernate集成
NHibernate集成 Nuget包 配置 实体映射 仓储 默认实现 自定义仓储 应用程序特定基础仓储类 ABP可以使用任何ORM框架,它内置集成NHibernate.此文档将讲解ABP如何使用NH ...
- Numpy基础学习
Numpy(Numerical Python的简称)是高性能科学计算和数据分析的基础包. 主要的功能: 1.ndarray,一个具有矢量运算和复杂广播工能的快速且节省空间的多维数组 2.用于对整组数据 ...
- java.util.logging.Logger基础
1. 定义 java.util.logging.Logger是Java自带的日志类,可以记录程序运行中所产生的日志.通过查看所产生的日志文件,可以分析程序的运行状况,出现异常时,分析及定位异常. 2. ...
- 2017人生总结(MECE分析法)
试着用MECE分析法对人生的整个规划做一下总结.作为技术人员,其实除了编码架构能力之外,分析问题的能力的重要程度也会随着职业发展越来越重要.<美团点评技术博客>说这几天要在黄金时段头版头条 ...
- [实例]JAVA调用微信接口发送图文消息,不用跳到详情页
package com.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStrea ...