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

题目大意:给一个只包含前9个字符的字符串,q次询问区间本质不同的子序列数。 n , q 1 0 5 n,q\le10^5
题解:
考虑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)
如果看做矩乘转移,则 A i A_i 这个矩阵是 A i , i = A c , i = 1 A_{i,i}=A_{c,i}=1 ,其余位置是0.
其显然有逆矩阵,并且逆矩阵 B i B_i 为, B i , i = 1 , B c , i = 1 ( c ̸ = i ) B_{i,i}=1,B_{c,i}=-1(c\not=i)
然后 a n s w e r ( L , R ) \mathrm{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]
然后你注意到,左乘一个 A i A_i 等价于,第c行的每个数字变成其所在列的和。
右乘一个 B i B_i 等价于,对于每一行所有不是第c列的数字,都要减去该行的第c列的数字,这个维护一个加法标记即可。
最后左面的那个行向量关心的是A的前缀积的每一列的和,这个已经维护好了;而最右面的那个列向量关心的是B的前缀积的最后一列,这个也可以 O ( m ) O(m) 提取。最后保留前缀积乘以那个向量的结果,就可以 O ( m ) O(m) 的回答每一个询问。
复杂度 O ( ( n + q ) m ) O((n+q)m) ,空间 O ( m 2 + n m ) O(m^2+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();
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/84922219
今日推荐