「2017 山东一轮集训 Day6」子序列 - dp - 矩阵乘法

题目大意:给一个只包含前9个字符的字符串,q次询问区间本质不同的子序列数。 n , q ≤ 1 0 5 n,q\le10^5 n,q105
题解:
考虑dp,设dp[i,j]表示前i个数字以j结尾的方案数。假设当前的是数字是c,那么:
d p ( i , c ) = 1 + ∑ k d p ( i − 1 , k ) , e l s e   d p ( i , j ) = d p ( i − 1 , j ) dp(i,c)=1+\sum_{k}dp(i-1,k),\mathrm{else\ }dp(i,j)=dp(i-1,j) dp(i,c)=1+kdp(i1,k),else dp(i,j)=dp(i1,j)
如果看做矩乘转移,则 A i A_i Ai这个矩阵是 A i , i = A c , i = 1 A_{i,i}=A_{c,i}=1 Ai,i=Ac,i=1,其余位置是0.
其显然有逆矩阵,并且逆矩阵 B i B_i Bi为, B i , i = 1 , B c , i = − 1 ( c ̸ = i ) B_{i,i}=1,B_{c,i}=-1(c\not=i) Bi,i=1,Bc,i=1(c̸=i)
然后 a n s w e r ( L , R ) \mathrm{answer(L,R)} answer(L,R)是:
[ 1 1 1 … 1 1 ] A R A R − 1 ⋯ A 1 B 1 B 2 ⋯ B L − 1 [ 0 0 . . . 0 1 ] \left[\begin{matrix} 1&1&1\dots1&1 \end{matrix}\right] A_RA_{R-1}\cdots A_1B_1B_2\cdots B_{L-1} \left[\begin{matrix} 0\\0\\.\\.\\.\\0\\1 \end{matrix}\right] [11111]ARAR1A1B1B2BL100...01
然后你注意到,左乘一个 A i A_i Ai等价于,第c行的每个数字变成其所在列的和。
右乘一个 B i B_i Bi等价于,对于每一行所有不是第c列的数字,都要减去该行的第c列的数字,这个维护一个加法标记即可。
最后左面的那个行向量关心的是A的前缀积的每一列的和,这个已经维护好了;而最右面的那个列向量关心的是B的前缀积的最后一列,这个也可以 O ( m ) O(m) O(m)提取。最后保留前缀积乘以那个向量的结果,就可以 O ( m ) O(m) O(m)的回答每一个询问。
复杂度 O ( ( n + q ) m ) O((n+q)m) O((n+q)m),空间 O ( m 2 + n m ) O(m^2+nm) O(m2+nm)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
	const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;
	char gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
	inline int inn()
	{
		int x,ch;while((ch=gc())<'0'||ch>'9');
		x=ch^'0';while((ch=gc())>='0'&&ch<='9')
			x=(x<<1)+(x<<3)+(ch^'0');return x;
	}
}using INPUT_SPACE::inn;
namespace OUTPUT_SPACE{
	char ss[1500000],tt[20];int ssl,ttl;
	inline int PC(char c) { return ss[++ssl]=c; }
	inline int print(lint x)
	{
		if(!x) ss[++ssl]='0';
		for(ttl=0;x;x/=10) tt[++ttl]=char(x%10+'0');
		for(;ttl;ttl--) ss[++ssl]=tt[ttl];return ss[++ssl]='\n';
	}
	inline int Flush() { return fwrite(ss+1,sizeof(char),ssl,stdout),0; }
}using OUTPUT_SPACE::print;using OUTPUT_SPACE::PC;using OUTPUT_SPACE::Flush;
const int m=10,M=m+5,N=100010;
int A[M][M],B[M][M],As[M],dlt[M];
int Ap[N][M],Bp[N][M],s[N];char str[N];
int main()
{
	scanf("%s",str+1);int n=int(strlen(str+1));
	rep(i,1,n) s[i]=str[i]-'a'+1;
	rep(i,1,m) A[i][i]=1,As[i]=Ap[0][i]=1;
	rep(i,1,n)
	{
		int c=s[i],Acj;
		rep(j,1,m) Acj=A[c][j],A[c][j]=As[j],As[j]+=As[j]-Acj,
			(As[j]>=mod?As[j]-=mod:0),(As[j]<0?As[j]+=mod:0);
		memcpy(Ap[i],As,sizeof(int)*(m+1));
	}
	rep(i,1,m) B[i][i]=1,dlt[i]=0,Bp[0][i]=int(i==m); 
	rep(i,1,n)
	{
		int c=s[i],t;
		rep(j,1,m) t=B[j][c]-dlt[j],(t<0?t+=mod:0),
			dlt[j]+=t,(dlt[j]>=mod?dlt[j]-=mod:0),
			B[j][c]+=t,(B[j][c]>=mod?B[j][c]-=mod:0);
		rep(j,1,m) Bp[i][j]=B[j][m]-dlt[j],(Bp[i][j]<0?Bp[i][j]+=mod:0);
	}
	for(int q=inn();q;q--)
	{
		int L=inn(),R=inn();lint ans=mod-1;
#define P(x) (lint)Ap[R][x]*Bp[L-1][x]
		ans=ans+P(1)+P(2)+P(3)+P(4)+P(5)+P(6)+P(7),ans%=mod;
		ans=ans+P(8)+P(9)+P(10),ans%=mod,print(int(ans));
	}
	return Flush();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值