题目链接
题目思路
本质上就是一堆容斥搞一搞
直接放官方题解
唯一有点描述不准确的是第二行里\(dp[k][i][j]\)表示的应该是\(x\)字母在前\(k\)个字符的所有\(ixj\)序列个数
代码
#include<bits/stdc++.h>
#define double long double
#define ll long long
#define fi first
#define se second
using namespace std;
const int maxn=8e4+5;
int n,q;
char s[maxn];
vector<int> g[30];
ll pre1[maxn][27];
// pre1[i][j] 表示前i个元素有多少个j
ll pre2[maxn][27][27];
// pre2[i][j][k] 表示前i个元素有多少个 jk子序列
ll dp[maxn][27][27],tmp[30][30][30];
//对于第 k 个字符是x ,字符串的前 k 个字符里,包含子序列 "ixj" 的数量是 dp[k][i][j] 。注意,这里的 "ixj" 中的 i 和 j 分别代表第 i 个字母和第 j 个字母
// 说前k个有点不严谨,因为其实知识x为前k个,还包含了后面的字符
ll cal(int l,int r,int a,int b){// 计算[l,r]区间有多少个ab子序列
return pre2[r][a][b]-pre2[l-1][a][b]-pre1[l-1][a]*(pre1[r][b]-pre1[l-1][b]);
}
signed main(){
scanf("%d%d %s",&n,&q,s+1);
for(int i=1;i<=26;i++){
g[i].push_back(0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=26;j++){
pre1[i][j]=pre1[i-1][j]+(s[i]=='a'+j-1);
}
for(int j=1;j<=26;j++){
for(int k=1;k<=26;k++){
pre2[i][j][k]=pre2[i-1][j][k];
if(s[i]=='a'+k-1){
pre2[i][j][k]+=pre1[i-1][j];
}
}
}
g[s[i]-'a'+1].push_back(i);
}
for(int i=1;i<=26;i++){
g[i].push_back(1e9);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=26;j++){
for(int k=1;k<=26;k++){
tmp[s[i]-'a'+1][j][k]+=pre1[i-1][j]*(pre1[n][k]-pre1[i][k]);
dp[i][j][k]=tmp[s[i]-'a'+1][j][k];
}
}
}
for(int i=1,l,r;i<=q;i++){
char t[10];
scanf("%d%d %s",&l,&r,t+1);
int a=t[1]-'a'+1;
int b=t[2]-'a'+1;
int c=t[3]-'a'+1;
if(pre1[r][b]-pre1[l-1][b]==0){
printf("0\n");
continue;
}
int pos1=lower_bound(g[b].begin(),g[b].end(),l)-g[b].begin();
int pos2=upper_bound(g[b].begin(),g[b].end(),r)-g[b].begin();
ll ans=dp[g[b][pos2-1]][a][c]-dp[g[b][pos1-1]][a][c];
ans-=pre1[l-1][a]*cal(l,r,b,c)+pre1[l-1][a]*(pre1[r][b]-pre1[l-1][b])*(pre1[n][c]-pre1[r][c])+cal(l,r,a,b)*(pre1[n][c]-pre1[r][c]);
printf("%lld\n",ans);
}
return 0;
}