题目描述

  有一个由前 \(m\) 个小写字母组成的串 \(S\),有 \(q\) 个询问,每次给你 \(l,r\),问你 \(S_{l\ldots r}\) 有多少个非空子序列。

  \(m=9,n=\lvert S\rvert \leq {10}^5,q\leq {10}^5\)

题解

  题解接下来的部分求得答案是包含空串的答案。

  最简单的做法是DP。

  设 \(f_{i,j}\) 为前 \(i\) 个字符,末尾为 \(j\) 的子序列个数。

  特殊的,如果 \(j=m+1\) 就说明当前还没有选任何字符。

  递推式为

\[f_{i,j}=
\begin{cases}
f_{i-1,j}&,j\neq S_i\\
\sum_{k=1}^{m+1}f_{i-1,k}&,j=S_i
\end{cases}
\]

  答案为 \(\sum_{i=1}^{m+1}f_{n,i}\)

  复杂度为 \(O(nq)\)。

  注意到这个转移是一个矩阵乘法的形式,记

\[F_i=
\begin{pmatrix}
f_{i,1}\\f_{i,2}\\\vdots\\f_{i,m+1}
\end{pmatrix}
\]

  那么

\[\begin{align}
F_i&=A_iF_{i-1}\\
A_i&=
\begin{pmatrix}
1&&&&\\
&1&&&\\
1&1&1&1&1\\
&&&1&\\
&&&&1
\end{pmatrix}
\end{align}
\]

  这里 \(A_i\) 就是把单位矩阵的第 \(S_i\) 行全部设为一得到的矩阵。

  记

\[\begin{align}
U&=
\begin{pmatrix}
1\\1\\\vdots\\1
\end{pmatrix}\\
V&=
\begin{pmatrix}
0\\0\\\vdots\\1
\end{pmatrix}
\end{align}
\]

  那么我们最终的答案就是

\[\begin{align}
&VA_rA_{r-1}\cdots A_lU\\
=&VA_rA_{r-1}\cdots A_1{A_1}^{-1}{A_2}^{-1}\ldots A_{l-1}U
\end{align}
\]

  我们可以维护一个 \(A\) 的前缀积和 \(A\) 的逆的前缀积即可。需要注意矩阵乘法的顺序。

  时间复杂度:\(O(nm^3+qm^2)\)

  可以发现,做矩阵乘法的时候只有一行有变化,那么预处理的矩阵乘法的复杂度可以降到 \(O(nm^2)\)。总复杂度为 \(O((n+q)m^2)\)

  其实这道题还能进一步优化。

  对于询问,我们只需要在预处理的时候把矩阵乘以 \(U\) 或 \(V\) 的结果保存下来,就可以做到 \(O(qm)\)。

  对于预处理,求 \(A_rA_{r-1}\cdots A_1\) 的时候左乘转移矩阵的时候实际上是把 \(S_i\) 这一行中每个位置的值改为这一列所有元素的和,直接维护一下每列的和就好了。右乘转移矩阵的逆矩阵就是对于每一行,除了 \(S_i\) 这一列外其他所有列都减掉这个位置。那么可以维护一下这一列所有元素共同减掉的数,然后修改一下这个位置单点的值。

  具体来说,假设原来的矩阵的某一行 \(0\) 是长这样:

\[\begin{pmatrix}
a_1-v&a_2-v&a_3-v&a_4-v\\
\end{pmatrix}
\]

  对第三个位置操作后就会变成

\[\begin{pmatrix}
a_1-a_3&a_2-a_3&(2a_3-v)-a_3&a_4-a_3
\end{pmatrix}
\]

  这样预处理的复杂度就降到了 \(O(nm)\)

  总复杂度为 \(O((n+q)m)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
#include<vector>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int p=1000000007;
const int N=100010;
const int M=10;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
const ll inv2=fp(2,p-2);
int plus(int a,int b)
{
a+=b;
return a>=p?a-p:a;
}
int plus2(int a)
{
a+=a;
return a>=p?a-p:a;
}
int minus(int a,int b)
{
a-=b;
return a<0?a+p:a;
}
int c[N];
int n,q;
int f1[N][10];
int a1[10][10];
int f2[N][10];
int a2[10][10];
char str[N];
void init()
{
for(int i=0;i<=9;i++)
a1[i][i]=a2[i][i]=f1[0][i]=1;
for(int i=1;i<=n;i++)
{
int v=str[i]-'a';
for(int j=0;j<=9;j++)
{
f1[i][j]=minus(plus2(f1[i-1][j]),a1[v][j]);
a1[v][j]=f1[i-1][j];
f2[i][j]=a2[v][j];
a2[v][j]=minus(plus2(a2[v][j]),f2[i-1][j]);
}
}
}
int main()
{
scanf("%s",str+1);
n=strlen(str+1);
scanf("%d",&q);
init();
int l,r;
for(int i=1;i<=q;i++)
{
l=rd();
r=rd();
int ans=f1[r][9]-1;
for(int j=0;j<=8;j++)
ans=(ans-(ll)f1[r][j]*f2[l-1][j])%p;
ans=plus(ans,p);
printf("%d\n",ans);
}
return 0;
}

【LOJ6074】【2017 山东一轮集训 Day6】子序列 DP的更多相关文章

  1. LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)

    题目链接 参考yww的题解.本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了. 不知不觉怎么写了这么多... 另外还是有莫队做法的...(虽然可能卡不过) \(60\)分的\(O(n^ ...

  2. LOJ #6074. 「2017 山东一轮集训 Day6」子序列

    #6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...

  3. loj#6074. 「2017 山东一轮集训 Day6」子序列(矩阵乘法 dp)

    题意 题目链接 Sol 设\(f[i][j]\)表示前\(i\)个位置中,以\(j\)为结尾的方案数. 转移的时候判断一下\(j\)是否和当前位置相同 然后发现可以用矩阵优化,可以分别求出前缀积和逆矩 ...

  4. 「2017 山东一轮集训 Day6」子序列(矩阵快速幂)

    /* 找出了一个dp式子 是否能够倍增优化 我推的矩阵不太一样 是 1 0 0 0 0 0 0 0 0 -1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 2 求得逆矩阵大概就是 1 0 0 ...

  5. loj#6076「2017 山东一轮集训 Day6」三元组 莫比乌斯反演 + 三元环计数

    题目大意: 给定\(a, b, c\),求\(\sum \limits_{i = 1}^a \sum \limits_{j = 1}^b \sum \limits_{k = 1}^c [(i, j) ...

  6. LOJ#6075. 「2017 山东一轮集训 Day6」重建

    题目描述: 给定一个 n个点m 条边的带权无向连通图 ,以及一个大小为k 的关键点集合S .有个人要从点s走到点t,现在可以对所有边加上一个非负整数a,问最大的a,使得加上a后,满足:s到t的最短路长 ...

  7. 【LOJ6067】【2017 山东一轮集训 Day3】第三题 FFT

    [LOJ6067][2017 山东一轮集训 Day3]第三题 FFT 题目大意 给你 \(n,b,c,d,e,a_0,a_1,\ldots,a_{n-1}\),定义 \[ \begin{align} ...

  8. Loj #6069. 「2017 山东一轮集训 Day4」塔

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

  9. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

随机推荐

  1. nginx代理天地图做缓存解决跨域问题

    作为一个GISer开发者,天地图是经常在项目中以底图的形式出现,其加载地址如: 天地图矢量:http://t{0-6}.tianditu.com/DataServer?T=vec_w&x={x ...

  2. JPasswordField密码框,JList列表框

    [JPasswordField密码框] //导入Java类 import javax.swing.*; import java.awt.*; import java.awt.event.ActionE ...

  3. Vue一个案例引发的递归组件的使用

    今天我们继续使用 Vue 的撸我们的实战项目,只有在实战中我们才会领悟更多,光纸上谈兵然并卵,继上篇我们的<Vue一个案例引发的动态组件与全局事件绑定总结> 之后,今天来聊一聊我们如何在项 ...

  4. ORA-00904: "WMSYS"."WM_CONCAT": invalid identifier

    同事玩Docker,在Docker里面启了一个Oracle 10g Express版本,在测试过程中遇到了ORA-00904: "WMSYS"."WM_CONCAT&qu ...

  5. 播放器的书签--推荐使用Potplayer

    VLC Player https://www.vlchelp.com/skipping-and-playing-audio-and-video-portions-in-vlc/ PotPlayer  ...

  6. LeetCode算法题-Minimum Moves to Equal Array Elements(Java实现)

    这是悦乐书的第233次更新,第246篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第100题(顺位题号是453).给定大小为n的非空整数数组,找到使所有数组元素相等所需的 ...

  7. 基于tcp的云盘上传下载的模拟

    老师的博客: server端 import json import struct import json import struct import socket import os sk = sock ...

  8. Ambari——大数据平台的搭建利器之进阶篇

    前言 本文适合已经初步了解 Ambari 的读者.对 Ambari 的基础知识,以及 Ambari 的安装步骤还不清楚的读者,可以先阅读基础篇文章<Ambari——大数据平台的搭建利器>. ...

  9. 网络二十四题 之 P2756 飞行员配对方案问题

    题目背景 第二次世界大战时期.. 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外 ...

  10. 日志级别的选择:Debug、Info、Warn、Error

    日志信息分类 1.等级由低到高:debug<info<warn<Error: 2.区别: debug 级别最低,可以随意的使用于任何觉得有利于在调试时更详细的了解系统运行状态的东东: ...