ccpc 网络赛 hdu 6155
# ccpc 网络赛 hdu 6155(矩阵乘法 + 线段树)
题意:
给出 01 串,要么询问某个区间内不同的 01 子序列数量,要么把区间翻转。
叉姐的题解:
先考虑怎么算 \(s_1, s_2, \ldots, s_n\)的答案。
设 \(dp(i, 0/1)\) 表示考虑到 \(s_i\)
,以 \(0/1\) 结尾的串的数量。
那么 \(dp(i, 0) =dp(i - 1, 0) + dp(i - 1, 1) + 1\),\(1\)也同理。
那么假设在某个区间之前,\(dp(i, 0/1) = (x, y)\) 的话,过了这段区间,就会变成 \((ax + by + c, dx + ey + f)(ax+by+c,dx+ey+f)\) 的形式,只要用线段树维护这个线性变化就好了。
fzu 2129 子序列的个数 做过这道题的话 dp方程应该写的出来
一直在思考如何区间合并这个东西,或者区间是否具有加减性质,可以的话就可以用线段树做了,然而并不能
维护矩阵真的是涨姿势了
先列出转移矩阵把
如果\(s_i = 0\),则有
\(dp[i][0] = 1 * dp[i-1][0] + 1 * dp[i-1][1] + 1 * 1\)
\(dp[i][1] = 0 * dp[i-1][0] + 1 * dp[i-1][1] + 0 * 1\)
否则
\(dp[i][0] = 1 * dp[i-1][0] + 0 * dp[i-1][1] + 0 * 1\)
\(dp[i][1] = 1 * dp[i-1][0] + 1 * dp[i-1][1] + 1 * 1\)
初始化\(dp[0][0/1] = 0\),
初始矩阵就为
\(\begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}\)
如果当前为0,矩阵是这样的
\(\begin{bmatrix} 1& 1 & 1\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix}\)
否则就是这样的
\(\begin{bmatrix} 1& 0 & 0\\ 0& 1 & 1\\ 0& 0 & 1 \end{bmatrix}\)
假设当前字符串为01,那么最后矩阵就是
\(\begin{bmatrix} 1& 0 & 0\\ 0& 1 & 1\\ 0& 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 1 & 1\\ 0& 1 & 0\\ 0& 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix} = \begin{bmatrix} 1\\ 2\\ 1 \end{bmatrix}\)
通过1和0的转移矩阵容易知道区间翻转其实就是把矩阵的某些元素交换一下
然后写个线段树区间合并就好了
#include<bits/stdc++.h> #define LL long long #define ls rt<<1 #define rs (rt<<1|1) using namespace std; void read(int &x){ char c = getchar(); x = 0; while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar(); } const int mod = 1e9 + 7; const int N = 1e5 + 10; struct MAT{ int a[3][3]; void change(){ swap(a[0][2],a[1][2]); swap(a[0][0],a[1][1]); swap(a[0][1],a[1][0]); } }s[N << 2]; void add(int &x,int y){ x += y; if(x >= mod) x -= mod; } MAT mul(MAT A,MAT B){ MAT ans; for(int i = 0;i < 3;i++){ for(int j = 0;j < 3;j++){ ans.a[i][j] = 0; for(int k = 0;k < 3;k++){ add(ans.a[i][j],1LL * A.a[i][k] * B.a[k][j]%mod); } } } return ans; } MAT mat[3]; int col[N << 2]; char S ; void init(){ for(int k = 0;k < 3;k++) for(int i = 0;i < 3;i++) for(int j = 0;j < 3;j++) mat[k].a[i][j] = i == j?1:0; mat[1].a[0][1] = mat[1].a[0][2] = 1; mat[2].a[1][0] = mat[2].a[1][2] = 1; } void pushdown(int rt){ if(col[rt]){ col[rs] ^= 1; col[ls] ^= 1; s[rs].change(); s[ls].change(); col[rt] = 0; } } void flip(int L,int R,int l,int r,int rt){ if(L <= l && R >= r){ col[rt] ^= 1; s[rt].change(); return ; } pushdown(rt); int m = l + r >> 1; if(L <= m) flip(L,R,l,m,ls); if(R > m) flip(L,R,m+1,r,rs); s[rt] = mul(s[rs], s[ls]); } MAT query(int L,int R,int l,int r,int rt){ if(L <= l && R >= r) return s[rt]; pushdown(rt); int m = l + r >> 1; if(L <= m && R > m) return mul(query(L,R,m+1,r,rs),query(L,R,l,m,ls)); if(L <= m) return query(L,R,l,m,ls); if(R > m) return query(L,R,m+1,r,rs); } void build(int l,int r,int rt){ col[rt] = 0; if(l == r){ s[rt] = mat[S[l] - '0' + 1]; col[rt] = 0; return ; } int m = l + r>>1; build(l,m,ls); build(m+1,r,rs); s[rt] = mul(s[rs], s[ls]); } int main(){ init(); int T,o,l,r,n,q; read(T); while(T--){ read(n),read(q); scanf("%s",S+1); build(1,n,1); while(q--){ read(o),read(l),read(r); if(o == 1) flip(l,r,1,n,1); else{ MAT ans = query(l,r,1,n,1); int tmp = (ans.a[0][2]+ans.a[1][2])%mod; printf("%d\n",tmp); } } } return 0; }
- HDU5842——Lweb and String(CCPC网络赛第11题)
- 2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6155 Subsequence Count 矩阵快速幂
- hdu 5838 Mountain(2016 CCPC网络赛1007) 状压
- hdu 5840 This world need more Zhu (2016CCPC 网络赛1009) 分块+线段树
- HDU 6153 A Secret CCPC网络赛,KMP拓展应用
- HDU-5832-A water problem【2016CCPC网络赛】
- hdu 5833 Zhu and 772002 ccpc网络赛 高斯消元法
- HDU-5835-Danganronpa【2016CCPC网络赛】
- HDU-5842-Lweb and String【2016CCPC网络赛】
- HDU 5832——A water problem & 2016CCPC网络赛1001
- A Secret 2017 CCPC 网络选拔赛 hdu 6153
- hdu 6152 : Friend-Graph (2017 CCPC网络赛 1003)
- HDU 5842—— Lweb and String & CCPC 网络赛 1011
- HDU-5833-Zhu and 772002【2016CCPC网络赛】【高斯消元】
- CCPC网络赛,HDU_5842 Lweb and String
- 简单规律 HDU - 6154 CaoHaha's staff[2017 CCPC网络选拔赛]
- Hdu 6156 Palindrome Function 2017 CCPC网络赛
- hdu 5929 Basic Data Structure(山东CCPC)
- hdu5832-ccpc网络赛 -高精度取余
- hdu 4050 2011北京赛区网络赛K 概率dp ***