[HDU6155]Subsequence Count

题目大意:
  给定一个01序列,支持以下两种操作:
    1.区间反转;
    2.区间求不同的子序列数量。

思路:
  首先我们考虑区间反转,这是一个经典的线段树操作。
  接下来考虑求不同的子序列数量,在已知当前区间的情况下,我们有如下$O(n)$的动态规划:|
    $f_{i,0}=f_{i-1,0}+f_{i-1,1}+1,f_{i,1}=f_{i-1,1}//第i位为0$
    $f_{i,1}=f_{i-1,0}+f_{i-1,1}+1,f_{i,0}=f_{i-1,0}//第i位为1$
  这样的动态规划显然无法直接用线段树维护,而如果不能直接用线段树维护,上面维护的区间反转也就失去了意义。
  为了使用线段树维护这种动态规划,我们需要用矩阵表示这种递推关系。
  $\left(\begin{array}{}f_{i+1,0}\\f_{i+1,1}\\1\end{array}\right)=\left(\begin{array}{}f_{i-1,0}\\f_{i-1,1}\\1\end{array}\right)\times\left(\begin{array}{}1&0&0\\1&1&0\\1&0&1\end{array}\right)$
  $\left(\begin{array}{}f_{i+1,0}\\f_{i+1,1}\\1\end{array}\right)=\left(\begin{array}{}f_{i-1,0}\\f_{i-1,1}\\1\end{array}\right)\times\left(\begin{array}{}1&1&0\\0&1&0\\0&1&1\end{array}\right)$
  这样我们就可以保存每个区间的乘积,询问时直接相乘即可。

  1 #include<cstdio>
  2 #include<cctype>
  3 #include<algorithm>
  4 inline int getint() {
  5     char ch;
  6     while(!isdigit(ch=getchar()));
  7     int x=ch^‘0‘;
  8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘);
  9     return x;
 10 }
 11 inline int getdigit() {
 12     char ch;
 13     while(!isdigit(ch=getchar()));
 14     return ch^‘0‘;
 15 }
 16 const int N=100001,mod=1e9+7;
 17 template<int SIZE>
 18 struct Matrix {
 19     int val[SIZE][SIZE];
 20     Matrix operator * (const Matrix &another) const {
 21         Matrix ret;
 22         for(int i=0;i<SIZE;i++) {
 23             for(int j=0;j<SIZE;j++) {
 24                 ret.val[i][j]=0;
 25                 for(int k=0;k<SIZE;k++) {
 26                     ret.val[i][j]+=(long long)val[i][k]*another.val[k][j]%mod;
 27                     ret.val[i][j]%=mod;
 28                 }
 29             }
 30         }
 31         return ret;
 32     }
 33     void operator *= (const Matrix &another) {
 34         *this=*this*another;
 35     }
 36     void flip() {
 37         std::swap(val[0][0],val[1][1]);
 38         std::swap(val[0][1],val[1][0]);
 39         std::swap(val[0][2],val[1][2]);
 40         std::swap(val[2][0],val[2][1]);
 41     }
 42     int calc() {
 43         return (val[2][0]+val[2][1])%mod;
 44     }
 45 };
 46 const Matrix<3> m[2]={
 47     {1,0,0,
 48      1,1,0,
 49      1,0,1},
 50     {1,1,0,
 51      0,1,0,
 52      0,1,1}
 53 };
 54 const Matrix<3> E={
 55     1,0,0,
 56     0,1,0,
 57     0,0,1
 58 };
 59 class SegmentTree {
 60     private:
 61         #define _left <<1
 62         #define _right <<1|1
 63         Matrix<3> val[N<<2];
 64         bool tag[N<<2];
 65         void push_up(const int p) {
 66             val[p]=val[p _left]*val[p _right];
 67         }
 68         void push_down(const int p) {
 69             if(!tag[p]) return;
 70             val[p _left].flip();
 71             val[p _right].flip();
 72             tag[p _left]^=true;
 73             tag[p _right]^=true;
 74             tag[p]=false;
 75         }
 76     public:
 77         void build(const int p,const int b,const int e) {
 78             tag[p]=false;
 79             if(b==e) {
 80                 val[p]=m[getdigit()];
 81                 return;
 82             }
 83             int mid=(b+e)>>1;
 84             build(p _left,b,mid);
 85             build(p _right,mid+1,e);
 86             push_up(p);
 87         }
 88         void modify(const int p,const int b,const int e,const int l,const int r) {
 89             if(b==l&&e==r) {
 90                 val[p].flip();
 91                 tag[p]^=true;
 92                 return;
 93             }
 94             push_down(p);
 95             int mid=(b+e)>>1;
 96             if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r));
 97             if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r);
 98             push_up(p);
 99         }
100         Matrix<3> query(const int p,const int b,const int e,const int l,const int r) {
101             if(b==l&&e==r) {
102                 return val[p];
103             }
104             push_down(p);
105             int mid=(b+e)>>1;
106             Matrix<3> ret=E;
107             if(l<=mid) ret*=query(p _left,b,mid,l,std::min(mid,r));
108             if(r>mid) ret*=query(p _right,mid+1,e,std::max(mid+1,l),r);
109             return ret;
110         }
111 };
112 SegmentTree t;
113 int main() {
114     for(int T=getint();T;T--) {
115         int n=getint(),q=getint();
116         t.build(1,1,n);
117         while(q--) {
118             int op=getint(),l=getint(),r=getint();
119             switch(op) {
120                 case 1: {
121                     t.modify(1,1,n,l,r);
122                     break;
123                 }
124                 case 2: {
125                     printf("%d\n",t.query(1,1,n,l,r).calc());
126                     break;
127                 }
128             }
129         }
130     }
131     return 0;
132 }
时间: 2024-10-09 13:05:57

[HDU6155]Subsequence Count的相关文章

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

DP式很容易得到,发现是线性递推形式,于是可以矩阵加速.又由于是区间形式,所以用线段树维护. https://www.cnblogs.com/Miracevin/p/9124511.html 关键在于证明区间操作中,可以直接在打标记的位置翻转矩阵两行两列. 上面网址用代数形式证了一遍,这里考虑从矩阵本身解释. 由线代内容可知,将一个矩阵作初等行变换,相当于将其左乘一个作了相应初等列变换的单位矩阵.同理将一个矩阵作初等列变换,相当于将其又乘一个作了相应初等行变换的单位矩阵. 这里,左乘的矩阵$T=

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

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

题目翻译 Description 给定一个\(01\)串 \(S_{1...n}\) 和 \(Q\) 个操作. 操作有两种类型: 1.将 \([l,r]\) 区间的数取反(将其中的\(0\)变成\(1\),\(1\)变成\(0\)). 2.询问字符串 \(S\) 的子串 \(S_{l...r}\) 有多少个不同的子序列.由于答案可能很大,请将答案对 \(10^9+7\) 取模. 在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列. Inp

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6155 题意: 题解来自:http://www.cnblogs.com/iRedBean/p/7398272.html 先考虑dp求01串的不同子序列的个数. dp[i][j]表示用前i个字符组成的以j为结尾的01串个数. 如果第i个字符为0,则dp[i][0] = dp[i-1][1] + dp[i-1][0] + 1,dp[i][1] = dp[i-1][1] 如果第i个字符为1,则dp[i][1

hdu 6155 -&#160;Subsequence Count

话说这题比赛时候过的好少,连题都没读TOT 先考虑dp求01串的不同子序列的个数. dp[i][j]表示用前i个字符组成的以j为结尾的01串个数. 如果第i个字符为0,则dp[i][0] = dp[i-1][1] + dp[i-1][0] + 1,dp[i][1] = dp[i-1][1] 如果第i个字符为1,则dp[i][1] = dp[i-1][1] + dp[i-1][0] + 1,dp[i][0] = dp[i-1][0] 显然这是线性递推,我们考虑如何用矩阵表示这种递推关系. 下面分别

Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵

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 queries:1. Flipping the bits (i.e., changing all 1 to 0 and 0 to 1) between l and r (inclusive).2. Counting the

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(矩阵 + 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] + dp[i -1][0] (0结尾新串) + dp[i - 1][0] (0结尾旧串) - dp[i - 1][0] (重复) + 1(0本身被重复时去除). 那么可以得到转移时的矩阵 $$ \left( \begin{matrix} dp[i - 1][0] & dp[i - 1][1] &am

CCPC for UESTC

1001. Vertex Cover 题意:有一个贪心算法求最小顶点覆盖是每次选出度数最大的点然后删去,输出一个图使得这个算法跑出来的答案是你给出的答案的三倍及以上. 题解: 构造一个二分图,设左边有 nn 个点,标号是 1 ~ n1 n.对于每个 i \in [1, n]i∈[1,n],都在右边新建 \lfloor \frac{n}{i} \rfloor??i??n??? 个点,每个点都选择左边的 ii个点连 1 条边,使得左边每个点最多只被多加了一条边.这样构造完成后可以发现贪心的做法会把右