「2017 山東一輪集訓 Day6」子序列 - dp - 矩陣乘法
阿新 • • 發佈:2018-12-12
題目大意:給一個只包含前9個字元的字串,q次詢問區間本質不同的子序列數。
。
題解:
考慮dp,設dp[i,j]表示前i個數字以j結尾的方案數。假設當前的是數字是c,那麼:
如果看做矩乘轉移,則
這個矩陣是
,其餘位置是0.
其顯然有逆矩陣,並且逆矩陣
為,
。
然後
是:
然後你注意到,左乘一個
等價於,第c行的每個數字變成其所在列的和。
右乘一個
等價於,對於每一行所有不是第c列的數字,都要減去該行的第c列的數字,這個維護一個加法標記即可。
最後左面的那個行向量關心的是A的字首積的每一列的和,這個已經維護好了;而最右面的那個列向量關心的是B的字首積的最後一列,這個也可以
提取。最後保留字首積乘以那個向量的結果,就可以
的回答每一個詢問。
複雜度
,空間
#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;