题目链接

参考yww的题解。本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了。

不知不觉怎么写了这么多...

另外还是有莫队做法的...(虽然可能卡不过)


\(60\)分的\(O(n^2)\)做法就是,令\(f[i]\)表示以\(s[i]\)结尾的不同子序列个数,\(las[c]\)表示\(c\)字符上次出现的位置(没有出现过则为\(-1\)),转移是:$$f[i]=\begin{cases}2f[i-1]+1&,las[s[i]]=-1\2f[i-1]-f[las[s[i]]]&,las[s[i]]\neq-1\end{cases}$$


上面这个做法挺妙的,但是好像没什么优化空间。

考虑另一种DP,令\(f[i][j]\)表示前\(i\)个字符,以字符\(j\)结尾的不同子序列个数。转移为:$$f[i][j]=\begin{cases}f[i-1][j-1]&,j\neq s_i\\sum_{k=0}^mf[i-1][k]&,j=s_i\end{cases}$$

其中字符集是\(0\sim m-1\)(\(m=9\)),特别的令\(f[i][m]\)表示前\(i\)个字符什么也没选的方案数,因为子序列可以从任意位置开始。

这样不会算重,感觉也挺妙的。。(好吧是自己菜)

显然可以用矩阵把转移表示出来。令$$F_i=\left[\begin{matrix}f_{i,1}\f_{i,2}\\vdots\f_{i,m+1}\end{matrix}\right]$$

转移矩阵就是\(A[i][i]=1,A[c][j]=1\ (c=s[i])\)(把单位矩阵\(s[i]\)那一行全设为\(1\))。就是这样子:$$F_i=A_iF_{i-1}\A_i=\left[\begin{matrix}1&&&&\&1&&&\1&1&1&1&1\&&&1&\&&&&1\end{matrix}\right]$$

再考虑一下初始化、最后的求和,令$$\begin{aligned}U&=\left[\begin{matrix}1\1\\vdots\1\end{matrix}\right]\V&=\left[\begin{matrix}0\ 0\ \cdots\ 0\ 1\end{matrix}\right]\end{aligned}$$

\(A_i\)显然有逆矩阵(可以比较容易地写出来)。那么区间\([l,r]\)的答案就是$$\begin{aligned}&UA_rA_{r-1}\cdots A_lV\=&UA_rA_{r-1}\cdots A_1{A_1}{-1}{A_2}{-1}\ldots A_{l-1}V\end{aligned}$$

这里把\(U\)放到了前面,\(V\)放到了后面,都一样,我觉得还是这样方便一些...

需要注意矩阵乘法没有交换律,注意乘的顺序。

所以预处理一个转移矩阵的前缀积\(f_i\)、转移矩阵逆元的前缀积\(g_i\),就可以\(O(m^3)\)回答一次询问了。

预处理\(f_i\)的时候让它和\(U\)乘一下,同理\(g_i\)和\(V\)乘一下,询问就是\(O(m)\)的了。

但是预处理的复杂度还是\(O(nm^3)\)的(但是开O2已经能过了...)。

注意到转移矩阵非常特殊,一个矩阵\(M\)乘上\(A_i\)时,\(M_{s_i,j}'\)是\(A_i\)第\(j\)列元素的和,\(M'\)其它行的元素不变。这样乘\(A_i\)可以做到\(O(m)\)。

同时左乘\(U\)得到的矩阵就是对列求和。

那么我们维护\(f_i\)的时候(乘了\(U\),是个\(1\times n\)的),第\(j\)列的和即\(f_{i,j}\),就是上一次第\(j\)列的和\(*2\)减去\(A_{s_i,j}\),上一次第\(j\)列的和就是\(f_{i-1,j}\)。那么这个转移也是\(O(m)\)的。

同理,\(A_i^{-1}\)大概是这样:$$A_i=\left[\begin{matrix}1&&&&\&1&&&\-1&-1&1&-1&-1\&&&1&\&&&&1\end{matrix}\right]$$

即\(A[i][i]=1,A[c][j]=-1\ (c=s[i],j\neq i)\)(把单位矩阵\(s[i]\)那一行除了\(A_{s[i],s[i]}\)全设为\(-1\))。

一个矩阵乘\(A_i^{-1}\)时,除了\(c=s[i]\)列之外的列\(M_{i,j}\),都会减去\(M_{i,c}\),第\(c\)列的元素不变。维护一个整行减了多少的标记,对\(M_{i,c}\)单点修改一下即可。

注意到假设其中一行是:\(\left[a_1-v\quad a_2-v\quad a_3-v\quad a_4-v\right]\),\(s[i]=3\)时,会变成\(\left[a_1-a_3\quad a_2-a_3\quad (2a_3-v)-a_3\quad a_4-a_3\right]\),也就是对\(3\)单独修改一下,所有数每次会减掉上次那个数,打个标记修改也是\(O(m)\)的了。

维护\(g_i\)时,注意到右乘一个\(V\)就是把矩阵最后一列取出来,直接求即可。同样\(O(m)\)。

那么总复杂度就做到\(O((n+q)m)\)啦。


//263ms	8760K
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rg register
#define mod 1000000007
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
#define Add2(x,y) (x+y>=mod?x+y-mod:x+y)
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+7,M=10;
const LL LIM=6e18; int s[N],f[N][M],g[N][M];
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
void Pre(const int n)
{
static int A[M][M],B[M][M],tag[M];
for(int i=0; i<M; ++i) A[i][i]=B[i][i]=f[0][i]=1;
g[0][M-1]=1;
for(rg int i=1; i<=n; ++i)
{
rg int c=s[i];
for(rg int j=0; j<M; ++j)
{
rg int tmp=f[i-1][j]<<1; Mod(tmp);
f[i][j]=Add2(tmp,mod-A[c][j]);
A[c][j]=f[i-1][j];
tmp=B[j][c]<<1, Mod(tmp); rg int tmp2=B[j][c];
g[i][j]=Add2(B[j][M-1],mod-B[j][c]);
B[j][c]=Add2(tmp,mod-tag[j]), tag[j]=tmp2;
}
}
} int main()
{
int n=0;
register char c; while(isalpha(c=gc())) s[++n]=c-'a';
Pre(n);
for(int q=read(); q--; )
{
int l=read()-1,r=read();
LL ans=0;
if(l) for(int i=0; i<M; ++i) ans+=1ll*f[r][i]*g[l][i], ans>=LIM&&(ans%=mod);
else ans=f[r][M-1];
printf("%lld\n",(ans-1)%mod);
} return 0;
}

LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)的更多相关文章

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

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

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

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

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

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

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

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

  5. LOJ.6066.[2017山东一轮集训Day3]第二题(树哈希 二分)

    LOJ 被一件不愉快的小事浪费了一个小时= =. 表示自己(OI方面的)智商没救了=-= 比较显然 二分+树哈希.考虑对树的括号序列进行哈希. 那么每个点的\(k\)子树的括号序列,就是一段区间去掉距 ...

  6. LOJ.6060.[2017山东一轮集训Day1/SDWC2018Day1]Set(线性基)

    LOJ BZOJ 明明做过一道(最初思路)比较类似的题啊,怎么还是一点思路没有. 记所有元素的异或和为\(s\),那么\(x_1+x_2=x_1+x_1\ ^{\wedge}s\). \(s\)是确定 ...

  7. 【LOJ6074】【2017 山东一轮集训 Day6】子序列 DP

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

  8. LOJ.6073.[2017山东一轮集训Day5]距离(可持久化线段树 树链剖分)

    题目链接 就是恶心人的,简单写写了...(似乎就是[HNOI2015]开店?) 拆式子,记\(dis_i\)为\(i\)到根节点的路径权值和,\(Ans=\sum dis_{p_i}+\sum dis ...

  9. LOJ.6068.[2017山东一轮集训Day4]棋盘(费用流zkw)

    题目链接 考虑两个\(\#\)之间产生的花费是怎样的.设这之间放了\(k\)个棋子,花费是\(\frac{k(k-1)}{2}\). 在\((r,c)\)处放棋子,行和列会同时产生花费,且花费和该行该 ...

随机推荐

  1. laravel 中CSS 预编译语言 Sass 快速入门教程

    CSS 预编译语言概述 CSS 作为一门样式语言,语法简单,易于上手,但是由于不具备常规编程语言提供的变量.函数.继承等机制,因此很容易写出大量没有逻辑.难以复用和扩展的代码,在日常开发使用中,如果没 ...

  2. java----重载

    重载: //同一个类中,方法名相同,参数列表不同[java就是靠不同的参数列表来寻找方法的],返回值可以任意,注意和函数的返回值类型相同.public class Demo { public stat ...

  3. node.js 的页面渲染方法ejs

    .安装依赖的组件 npm i consolidate -D npm i ejs -D 2.布局服务端 const express = require('express'); const consoli ...

  4. 3种vue路由传参的基本模式

    路由是连接各个页面的桥梁,而参数在其中扮演者异常重要的角色,在一定意义上,决定着两座桥梁是否能够连接成功. 在vue路由中,支持3中传参方式. 场景,点击父组件的li元素跳转到子组件中,并携带参数,便 ...

  5. 007-Python函数-装饰器

    函数回顾 1.函数可以当做一个参数赋值给另一个函数: def func(): print("in the func") def foo(x): x() foo(func) 输出: ...

  6. Choosing web framework: ASP.NET MVC vs Django Python vs Ruby on Rails(转载)

    来源:http://podlipensky.com/2012/06/choosing-web-framework-asp-net-mvc-vs-django-python-vs-ruby-on-rai ...

  7. UE4 ShooterGame Demo的开火的代码

    之前一直没搞懂按下鼠标左键开火之后,代码的逻辑是怎么走的,今天看懂了之前没看懂的部分,进了一步 ShooterCharacter.cpp void AShooterCharacter::OnStart ...

  8. One point compactification

    Theorem (One point compactification) Any locally compact space \(X\) can be embedded in another comp ...

  9. 有关centos7 图形化root用户登录

    好久不用的虚拟机开机后,是图形化登录界面,原来是命令行界面,后来安装和图形化 结果使用我记录的root密码死活登录不了,心想是不是时间久了忘记了root密码 然后开始尝试各种单用户修改root密码,再 ...

  10. spring security实现动态配置url权限的两种方法

    缘起 标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色. 基于s ...