1. 程式人生 > >「2017 山東一輪集訓 Day6」子序列 - dp - 矩陣乘法

「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;