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
1010
2 1 4
2 2 4
1 2 3
2 1 4
11
6
8

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列交换。

  回到线段树,如果要翻转,直接在对应区间维护的矩阵进行 行交换列交换,维护并下传标记即可。

  

  神题啊!


 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 typedef long long ll;
 5 const int N=100010,Mod=1e9+7;
 6 int n,q;
 7 char in[N];
 8 struct Mat{
 9     ll a[3][3];
10     void flip(){
11         for(int i=0;i<3;i++) swap(a[i][0],a[i][1]);
12         swap(a[0][0],a[1][0]);
13         swap(a[0][1],a[1][1]);
14     }
15     friend Mat operator * (Mat x,Mat y){
16         Mat ret;
17         for(int i=0;i<3;i++)
18             for(int j=0;j<3;j++){
19                 ret.a[i][j]=0;
20                 for(int k=0;k<3;k++)
21                     ret.a[i][j]=(ret.a[i][j]+x.a[i][k]*y.a[k][j])%Mod;
22             }
23         return ret;
24     }
25 };
26 const Mat stand[2]={{1,0,0,1,1,0,1,0,1},{1,1,0,0,1,0,0,1,1}};
27 struct SegmentTree{
28     int root,cnt,ch[N*4][2],rev[N*4];
29     Mat info[N*4];
30     void build(int &u,int l,int r){
31         if(!u) u=++cnt;
32         if(l==r){
33             info[u]=stand[in[l]=='1'];
34             return;
35         }
36         int mid=(l+r)>>1;
37         build(ch[u][0],l,mid);
38         build(ch[u][1],mid+1,r);
39         pushup(u);
40     }
41     void flip(int u,int l,int r,int L,int R){
42         if(L<=l&&r<=R){
43             rev[u]^=1;
44             info[u].flip();
45             return;
46         }
47         pushdown(u);
48         int mid=(l+r)>>1;
49         if(L<=mid) flip(ch[u][0],l,mid,L,R);
50         if(mid<R) flip(ch[u][1],mid+1,r,L,R);
51         pushup(u);
52     }
53     Mat query(int u,int l,int r,int L,int R){
54         if(L<=l&&r<=R) return info[u];
55         pushdown(u);
56         int mid=(l+r)>>1;
57         if(R<=mid) return query(ch[u][0],l,mid,L,R);
58         if(mid<L) return query(ch[u][1],mid+1,r,L,R);
59         return query(ch[u][0],l,mid,L,R)*query(ch[u][1],mid+1,r,L,R);
60     }
61     inline void pushup(int u){
62         info[u]=info[ch[u][0]]*info[ch[u][1]];
63     }
64     inline void pushdown(int u){
65         if(!rev[u]) return;
66         rev[ch[u][0]]^=1; rev[ch[u][1]]^=1;
67         info[ch[u][0]].flip(); info[ch[u][1]].flip();
68         rev[u]=0;
69     }
70 }seg;
71 int main(){
72     scanf("%d%d%s",&n,&q,in+1);
73     seg.build(seg.root,1,n);
74     int t,l,r;
75     while(q--){
76         scanf("%d%d%d",&t,&l,&r);
77         if(t==1)
78             seg.flip(seg.root,1,n,l,r);
79         else{
80             Mat ans=seg.query(seg.root,1,n,l,r);
81             printf("%lld\n",(ans.a[2][0]+ans.a[2][1])%Mod);
82         }
83     }
84     return 0;
85 }

奇妙代码

转载于:https://www.cnblogs.com/RogerDTZ/p/7632599.html

Subsequence Count (线段树)相关推荐

  1. 分门别类刷leetcode——高级数据结构(字典树,前缀树,trie树,并查集,线段树)

    目录 Trie树(字典树.前缀树)的基础知识 字典树的节点表示 字典树构造的例子 字典树的前序遍历 获取字典树中全部单词 字典树的整体功能 字典树的插入操作 字典树的搜索操作 字典树的前缀查询 字典树 ...

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

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

  3. HDU 6155 Subsequence Count(矩阵乘法+线段树+基础DP)

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

  4. 【XSY2538】/【HDU6155】Subsequence Count(矩阵乘法+线段树)

    题目翻译 Description 给定一个010101串 S1...nS_{1...n}S1...n​ 和 QQQ 个操作. 操作有两种类型: 1.将 [l,r][l,r][l,r] 区间的数取反(将 ...

  5. Count Color poj2777 线段树

    Count Color poj2777 线段树 题意 有一个长木板,现在往上面在一定区间内刷颜色,后来刷的颜色会掩盖掉前面刷的颜色,问每次一定区间内可以看到多少种颜色. 解题思路 这里使用线段树,因为 ...

  6. New Year and Old Subsequence CodeForces - 750E(线段树+矩阵dp)2019南昌icpc网络赛Hello 2019

    A string t is called nice if a string "2017" occurs in t as a subsequence but a string &qu ...

  7. 数据结构二之线段树Ⅰ——Count Color,Hotel,Transformation,Tree Generator™

    普通的下标线段树 Count Color Hotel Transformation Tree Generator™ Count Color POJ2777 查询区间内颜色种类数,观察到颜色种类数只有3 ...

  8. 线段树-Count on a Treap-神题

    Count on a Treap 题目来源 Codechef Feb 2014 COT5 https://www.codechef.com/problems/COT5 问题提出 什么是Treap 是一 ...

  9. poj 2777 Count Color(线段树区区+染色问题)

    题目链接:  poj 2777 Count Color 题目大意:  给出一块长度为n的板,区间范围[1,n],和m种染料 k次操作,C  a  b  c 把区间[a,b]涂为c色,P  a  b 查 ...

  10. POJ 2777 Count Color (线段树区间修改 + 状态压缩)

    题目链接:POJ 2777 Count Color [题目大意] 给你 n 块板子, 编号1--n , 板子的颜色最多30种, 初始时  板子的颜色都是 1: 有两种操作 1 .把给定区间的板子染成一 ...

最新文章

  1. php接入微信运动计步功能,运动计步,微信运动究竟靠不靠谱?
  2. jquery书写一个简易的二级联动
  3. MySql数据库explain用法示例_mysql explain的用法
  4. Linux下自动检测USB热插拔
  5. 网络相关配置,SSH服务,bash, 元字符
  6. java安装后在哪里打开_冷却塔声屏障安装后降噪效果不理想,原因出在哪里?...
  7. git commit --amend用法(摘抄)
  8. ssas如何创建分区_Analysis Services(SSAS)多维设计技巧–创建尺寸
  9. daysmatter安卓版_days matter
  10. android 内存抖动_android内存泄漏怎么破?一招教你搞定!
  11. varnish运行机制及管理优化
  12. Astah绘制UML图形
  13. 阿里云ACA、ACP、ACE认证考试区别,报名入口及模拟试题分享
  14. Unity 预编译选项
  15. 被互联网婚恋榨干的你,还会相信爱情吗?
  16. 史上最强的超级爆笑锦集
  17. SQLI DUMB SERIES-1
  18. 智慧屏与普通屏幕有何区别?
  19. 等保测评(linux)
  20. Sniper模拟器的安装

热门文章

  1. 揭秘2017双11背后的网络-双11的网络产品和技术概览
  2. 一名 40 岁“老”程序员的反思
  3. Anroid性能优化系列——Improving Layout Performance(一)
  4. PHP关闭自动过滤,php怎么关闭自动过滤输入和输出
  5. 以逗号分隔的正则表达式_再见,正则表达式
  6. 炼数成金数据分析课程---10、python中如何画图
  7. Ubuntu做Tomcat服务:insserv: warning: script 'tomcat' missing LSB tags and overrides
  8. java json字符串转成 Map或List
  9. 持久化/Session和SessionFactory线程非安全和安全/Hibernate的优势
  10. 带发送FIFO缓冲的RX232串口发送以及把众多文件变成“黑匣子”用于其它工程的调用...