【JZOJ 6008】Sequence/【JZOJ 5132】 子序列

58 篇文章 0 订阅
9 篇文章 0 订阅

2019/1/18 UPD:
【JZOJ 6008】Sequence是【JZOJ 5132】 子序列 的升级版,令串长为N,则N<=1e5,而前者字符集|c|<=52,后者|c|<=10
也即:问题“Q次询问长度为N的串S的子串S[l…r]中本质不同子序列个数”,强制在线,可以做到 O ( n ∣ c ∣ ) O(n|c|) O(nc)预处理+单次询问 O ( ∣ c ∣ ) O(|c|) O(c)的复杂度

Description

这里写图片描述
这里写图片描述

40%

dp,很简单所以不说了

100%

小写字母只有前9个,考虑抓住这个突破口
仔细观察dp式子
f [ i ] [ s [ i ] ] = 1 + ∑ c h f [ i − 1 ] [ c h ] f[i][s[i]]=1+\sum_{ch}f[i-1][ch] f[i][s[i]]=1+chf[i1][ch]
f [ i ] [ c h ] = f [ i − 1 ] [ c h ] f[i][ch]=f[i-1][ch] f[i][ch]=f[i1][ch]
这可以写成矩阵的形式,我们可以预处理出矩阵前缀积,逆矩阵前缀积
O ( n 1 0 3 + q 1 0 2 ) O(n10^3+q10^2) O(n103+q102)

Code

#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef ll mat[10][10];
const int N=1e5+5,mo=1e9+7;
int n,s[N];
ll A[10],B[10],C[10];
mat f[N],inv[10],g[N],cmat[10];
void read(int &t)
{
    t=0;int p=1;char ch;
    for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';t*=p;
}
ll qmi(ll x,ll n)
{
	ll t=1;
	for(;n;n>>=1,x=x*x%mo)
		if(n&1) t=t*x%mo;
	return t;
}
void mul(mat &c,mat a,mat b)
{
	mset(c,0);
	fo(i,0,9)
		fo(j,0,9)
			fo(k,0,9) c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mo;
}
void getinv(mat &c,mat b)
{
	mset(c,0);fo(i,0,9) c[i][i]=1;
	mat a;memcpy(a,b,sizeof(a));
	fo(i,0,9)
	{
		int p=-1;
		fo(j,i,9)
			if(a[j][i]) {p=j;break;}
		swap(a[i],a[p]),swap(c[i],c[p]);
		ll ny=qmi(a[i][i],mo-2);
		fo(j,i+1,9)
		{
			ll t=mo-a[j][i]*ny%mo;
			fo(k,i,9) a[j][k]=(a[j][k]+t*a[i][k])%mo,c[j][k]=(c[j][k]+t*c[i][k])%mo;
		}
	}
	fd(i,9,0)
	{
		ll ny=qmi(a[i][i],mo-2);
		fo(j,0,i-1)
		{
			ll t=mo-a[j][i]*ny%mo;
			fo(k,i,9) a[j][k]=(a[j][k]+t*a[i][k])%mo,c[j][k]=(c[j][k]+t*c[i][k])%mo;
		}
	}
}
int main()
{
	freopen("sub9.in","r",stdin);
	freopen("sub.out","w",stdout);
	for(char ch=getchar();'a'<=ch && ch<='z';ch=getchar()) s[++n]=ch-'a';
	fo(ch,0,8)
		fo(i,0,9) cmat[ch][i][i]=cmat[ch][i][ch]=1;
	fo(i,0,9) f[0][i][i]=inv[0][i][i]=g[0][i][i]=1;
	fo(i,1,n) mul(f[i],f[i-1],cmat[s[i]]);
	//fo(i,1,n) getinv(inv[i],f[i]);
	fo(i,0,8) getinv(inv[i],cmat[i]);
	fo(i,1,n) mul(g[i],inv[s[i]],g[i-1]);
	int l,r,Q;
	read(Q);
	while(Q--)
	{
		read(l),read(r);
		mset(A,0);A[9]=1;
		mset(B,0);
		fo(j,0,9)
			fo(k,0,9) B[j]=(B[j]+A[k]*g[l-1][k][j])%mo;
		mset(C,0);
		fo(j,0,9)
			fo(k,0,9) C[j]=(C[j]+B[k]*f[r][k][j])%mo;
		ll ans=0;
		fo(i,0,8) ans+=C[i];
		printf("%lld\n",ans%mo);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值