kmjp's blog

競技プログラミング参加記です

CSAcademy Round #24 : F. Subsequence Queries

ちょっとゴリ押し。
https://csacademy.com/contest/round-24/#task/subsequence-queries

問題

文字列SとQ個のクエリが与えられる。Sはa~iの9種の文字で構成される。
各クエリでは、Sの部分文字列S[L..R]が指定される。
その部分文字列に対し、異なる空でない部分列の組み合わせを求めよ。

解法

以下を考える。
F(n,c) := S[1..n]の部分列のうち、最後の文字がcのもの

F(n+1,c)は以下のようになる。

  • c==S[n+1]の時、その1文字を末尾に追加するケースを考える:F(n+1,c) = 1+sum_c'(F(n,c'))
  • c!=S[n+1]の時、その1文字を末尾に追加することはないので:F(n+1,c) = F(n,c)

上記式を10x10の行列に落とし込むことでF(n,c)とF(n+1,c')の関係を求められる。
この行列をA(n)としよう。
S[L..R]における解答は、A(R-1)*A(R-2)*...*A(L)*Pとなる。(Pは空ベクトル)。
上記を高速に求めるには、A(0)~A(R-1)の累積積?からA(0)~A(L-1)の累積積を割るか、SegTreeで連続する行列の積を求めていけば良い。

以下のコードは前者である。
計算時間が厳しかったので、インラインアセンブラで無理やり通した。
 

ll mo=1000000007;
ll mo2=4*mo*mo;

inline int mulmod(int a,int b,int mo) {
	int d,r;
	if(a==0 || b==0) return 0;
	if(a==1 || b==1) return max(a,b);
	__asm__("mull %4;"
	        "divl %2"
		: "=d" (r), "=a" (d)
		: "r" (mo), "a" (a), "d" (b));
	return r;
}

const int MAT=10;
struct Mat { ll v[MAT][MAT]; };
Mat mulmat(Mat& a,Mat& b,int n=MAT) {
	ll mo2=4*mo*mo;
	int x,y,z; Mat r;
	FOR(x,n) FOR(y,n) r.v[x][y]=0;
	FOR(x,n) FOR(z,n) FOR(y,n) {
		r.v[x][y] += a.v[x][z]*b.v[z][y];
		if(r.v[x][y]>mo2) r.v[x][y] -= mo2;
	}
	FOR(x,n) FOR(y,n) r.v[x][y]%=mo;
	return r;
}

ll modpow(ll a, ll n = mo-2) {
	ll r=1;
	while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1;
	return r;
}

Mat revmat(Mat a,int n=MAT) {
	Mat m;
	int x,y,z;
	FOR(x,n) FOR(y,n) m.v[x][y]=x==y;
	
	FOR(x,n) {
		for(y=x;y<n;y++) if(a.v[y][x]) break;
		assert(y!=n);
		FOR(z,n) swap(a.v[x][z],a.v[y][z]),swap(m.v[x][z],m.v[y][z]);
		ll r=modpow(a.v[x][x]);
		FOR(y,n) a.v[x][y]=mulmod(a.v[x][y],r,mo),m.v[x][y]=mulmod(m.v[x][y],r,mo);
		FOR(y,n) if(y!=x) {
			r = a.v[y][x];
			FOR(z,n) {
				m.v[y][z] -= mulmod(r,m.v[x][z],mo);
				if(m.v[y][z]<0) m.v[y][z]+=mo;
				a.v[y][z] -= mulmod(r,a.v[x][z],mo);
				if(a.v[y][z]<0) a.v[y][z]+=mo;
			}
		}
	}
	return m;
	
}

Mat M[101010],S[101010],R[101010];
int N,Q;
string SS;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>SS;
	N=SS.size();
	
	FOR(i,MAT) S[0].v[i][i]=R[0].v[i][i]=1;
	
	FOR(i,N) {
		FOR(x,MAT) M[i+1].v[x][x]=1;
		FOR(x,MAT) M[i+1].v[SS[i]-'a'][x]=1;
		S[i+1]=mulmat(S[i],M[i+1]);
		Mat r=revmat(M[i+1]);
		R[i+1]=mulmat(r,R[i]);
	}
	cin>>Q;
	while(Q--) {
		int LL,RR;
		cin>>LL>>RR;
		Mat r=mulmat(R[LL-1],S[RR]);
		ll ret=0;
		FOR(i,MAT-1) ret+=r.v[i][MAT-1];
		_P("%lld\n",ret%mo);
	}