[HDU6155]Subsequence Count(线段树+矩阵)

DP式很容易得到,发现是线性递推形式,于是可以矩阵加速。又由于是区间形式,所以用线段树维护。

https://www.cnblogs.com/Miracevin/p/9124511.html

关键在于证明区间操作中,可以直接在打标记的位置翻转矩阵两行两列。

上面网址用代数形式证了一遍,这里考虑从矩阵本身解释。

由线代内容可知,将一个矩阵作初等行变换,相当于将其左乘一个作了相应初等列变换的单位矩阵。同理将一个矩阵作初等列变换,相当于将其又乘一个作了相应初等行变换的单位矩阵。

这里,左乘的矩阵$T=\begin{bmatrix}0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1\end{bmatrix}$,右乘的矩阵$T‘$同样也是这个。

我们发现,$T\times T‘$就是单位矩阵,也就是说$T$的逆矩阵就是自己。

于是有,$T\times A\times T‘\times T\times B\times T‘=T\times A\times B\times T‘$。

这就说明中间的所有矩乘操作都可以被省略,只留下首尾的$T$和$T‘$。

这也就证明了,对区间矩阵积直接做变换是正确的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define ls (x<<1)
 5 #define rs (ls|1)
 6 #define lson ls,L,mid
 7 #define rson rs,mid+1,R
 8 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 9 using namespace std;
10
11 const int N=100010,mod=1e9+7;
12 bool tag[N<<2];
13 int n,Q,T,op,l,r;
14
15 struct Mat{ int a[3][3]; }v[N<<2];
16 const Mat D[2]={(Mat){1,0,0,1,1,0,1,0,1},(Mat){1,1,0,0,1,0,0,1,1}};
17 int calc(Mat a){ return (a.a[2][0]+a.a[2][1])%mod; }
18
19 Mat operator *(const Mat &a,const Mat &b){
20     Mat c; memset(c.a,0,sizeof(c.a));
21     rep(i,0,2) rep(j,0,2) rep(k,0,2)
22         c.a[i][k]=(c.a[i][k]+1ll*a.a[i][j]*b.a[j][k])%mod;
23     return c;
24 }
25
26 void put(int x){
27     tag[x]^=1;
28     swap(v[x].a[0][0],v[x].a[1][0]);
29     swap(v[x].a[0][1],v[x].a[1][1]);
30     swap(v[x].a[0][2],v[x].a[1][2]);
31     swap(v[x].a[0][0],v[x].a[0][1]);
32     swap(v[x].a[1][0],v[x].a[1][1]);
33     swap(v[x].a[2][0],v[x].a[2][1]);
34 }
35
36 void push(int x){ if (tag[x]) put(ls),put(rs),tag[x]=0; }
37
38 void build(int x,int L,int R){
39     tag[x]=0;
40     if (L==R){ char t; scanf(" %c",&t); v[x]=D[t-‘0‘]; return; }
41     int mid=(L+R)>>1; build(lson); build(rson); v[x]=v[ls]*v[rs];
42 }
43
44 void mdf(int x,int L,int R,int l,int r){
45     if (L==l && r==R){ put(x); return; }
46     int mid=(L+R)>>1; push(x);
47     if (r<=mid) mdf(lson,l,r);
48     else if (l>mid) mdf(rson,l,r);
49         else mdf(lson,l,mid),mdf(rson,mid+1,r);
50     v[x]=v[ls]*v[rs];
51 }
52
53 Mat que(int x,int L,int R,int l,int r){
54     if (L==l && r==R) return v[x];
55     int mid=(L+R)>>1; push(x);
56     if (r<=mid) return que(lson,l,r);
57     else if (l>mid) return que(rson,l,r);
58         else return que(lson,l,mid)*que(rson,mid+1,r);
59 }
60
61 int main(){
62     freopen("hdu6155.in","r",stdin);
63     freopen("hdu6155.out","w",stdout);
64     for (scanf("%d",&T); T--; ){
65         scanf("%d%d",&n,&Q); build(1,1,n);
66         while (Q--){
67             scanf("%d%d%d",&op,&l,&r);
68             if (op==1) mdf(1,1,n,l,r); else printf("%d\n",calc(que(1,1,n,l,r)));
69         }
70     }
71     return 0;
72 }

原文地址:https://www.cnblogs.com/HocRiser/p/10292914.html

时间: 2024-10-09 13:06:02

[HDU6155]Subsequence Count(线段树+矩阵)的相关文章

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] + 1 , f[i][0] = f[i-1][0]\) \(\ \ if(!A[i]) f[i][0] = f[i-1][0] + f[i-1][1] + 1 , f[i][1] = f[i-1][1]\) 很整齐,我们来写成矩阵的样子: \(f[i,0]\ f[i,1]\ 1=f[i-1,0]\ f[i

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

ZOJ 3772 Calculate the Function 线段树+矩阵

Calculate the FunctionTime Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Appoint description:  System Crawler  (2014-04-09) Description You are given a list of numbers A1A2 .. AN and M queries. For the i-th query

New Year and Old Subsequence CodeForces - 750E(线段树 + 矩阵)

New Year and Old Subsequence (CodeForces - 750E) 题意: 给出一个长度为\(N\)的数字串,\(q\)次询问.每次询问一段区间.在区间内删除尽量少的字符,使得区间内含有序列"\(2017\)",且不含有"\(2016\)". \(n,q<=200000\). 题解: 用\(01234\)五种状态分别表示"". "\(2\)"."\(20\)"."

线段树 + 矩阵 --- ZOJ 3772 Calculate the Function

Calculate the Function Problem's Link:   http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3772 Mean: 略 analyse: 简单的线段树维护矩阵. 矩阵乘法的结合律(a * b * c == a * (b * c)),注意矩阵乘法不满足分配率(a *b != b * a). 令 M[x] = [1 A[x]]              [1     0 ] ,那么有 [ F

hdu 5068(线段树+矩阵乘法)

矩阵乘法来进行所有路径的运算, 线段树来查询修改. 关键还是矩阵乘法的结合律. Harry And Math Teacher Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 326    Accepted Submission(s): 89 Problem Description As we all know, Harry Porter

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25

CF719E(线段树+矩阵快速幂)

题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这个数组 因为矩阵的可加性,所以可以进行lazy操作 我最开始的想法是每个节点lazy表示该区间下标加了多少,add表示该区间已经加的下标对应的矩阵乘积,这样更新lazy是O(1)的,算add是O(logn)的 但是这样每次pushdown的时候,add下传总要多个log,会TLE 更好的办法是laz

HDU 5068 Harry And Math Teacher 线段树+矩阵乘法

题意: 一栋楼有n层,每一层有2个门,每层的两个门和下一层之间的两个门之间各有一条路(共4条). 有两种操作: 0 x y : 输出第x层到第y层的路径数量. 1 x y z : 改变第x层 的 y门 到第x+1层的 z门的通断情况. 思路: 门之间的路径数可以用矩阵来表示,经过的中间层可以用矩阵乘积表示. 所以用线段树维护矩阵乘积即可. 代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring>