题目链接: https://nanti.jisuanke.com/t/41350
题意:
给你一个长为 2 e 6 2e6 2e6 的字符串,给你 2 e 6 2e6 2e6 个询问,每次询问一个区间 [ l , r ] [l,r] [l,r] ,问你在这个区间中,最少要删掉多少个字符才能使得这个区间中存在子序列 ′ 910 2 ′ '9102' ′9102′ 而不存在子序列 ‘ 8102 ’ ‘8102’ ‘8102’ 。
做法:
这里先贴一道cf上一个很相似的原题:https://codeforc.es/contest/750/problem/E。
这道题只要把条件转换一下,字符串前后倒置就是一样的做法了。
这里建立了一个长为 n n n 的线段树,每棵线段树维护矩阵乘法,复杂度 25 n l o g n 25nlogn 25nlogn。
什么叫维护矩阵乘法呢,其实应该更像加法的样子。如果我们没有这个所谓的区间查询,只是问你整个序列的话,其实就是一个 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字符在匹配从 i i i 这个位置到 j j j 这个位置的所删最少字符个数。
什么叫位置,我们将数字 01234 01234 01234 表示位置。
| 2 | 0 | 1 | 7 |
0–1-- 2 --3–4 (横线只是为了对齐)
线段树的每个节点存一个矩阵 A A A。 a i , j a_{i,j} ai,j表示使原串的子序列包含 ′ 201 9 ′ '2019' ′2019′ 中第 i i i 个间隔到第 j j j 个间隔组成的子串,但不包含严格包含它的子序列最少需要删除的数字.
如果这个位置上的字符是 ′ 1 ′ '1' ′1′,那么为了保证在出现位置 2 2 2 开始之后结尾还是在 2 2 2 ,就要删掉这个字符,即 a [ 2 ] [ 2 ] = 1 a[2][2] =1 a[2][2]=1 ,并且 a [ 2 ] [ 3 ] = 0 a[2][3] =0 a[2][3]=0。
然后就是加法进行转移了!因为我们是反了一下,所以查询的时候也转一下就好了。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=200005;
const int N=5;
struct node{
int a[N][N];
node operator + (node x){
node ret;
rep(i,0,4){
rep(j,0,4){
ret.a[i][j]=maxn;
rep(k,0,4){
ret.a[i][j]=min(ret.a[i][j],a[i][k]+x.a[k][j]);
}
}
}
return ret;
}
}e[maxn<<2];
char s[maxn];
int n,q;
void build(int l,int r,int rt){
if(l==r){
rep(i,0,4){
rep(j,0,4){
e[rt].a[i][j]=(i==j?0:maxn);
}
}
if(s[l]=='2') e[rt].a[0][1]=0,e[rt].a[0][0]=1;
if(s[l]=='0') e[rt].a[1][2]=0,e[rt].a[1][1]=1;
if(s[l]=='1') e[rt].a[2][3]=0,e[rt].a[2][2]=1;
if(s[l]=='9') e[rt].a[3][4]=0,e[rt].a[3][3]=1;
if(s[l]=='8') e[rt].a[3][3]=1,e[rt].a[4][4]=1;
return ;
}
build(l,mid,lson);
build(mid+1,r,rson);
e[rt]=e[lson]+e[rson];
}
node query(int l,int r,int rt,int ql,int qr){
if(ql<=l&&r<=qr){
return e[rt];
}
if(ql>mid) return query(mid+1,r,rson,ql,qr);
else if(qr<=mid) return query(l,mid,lson,ql,qr);
else return query(l,mid,lson,ql,qr)+query(mid+1,r,rson,ql,qr);
}
int main(){
scanf("%d%d%s",&n,&q,s+1);
for(int l=1,r=n;l<=r;l++,r--) swap(s[l],s[r]);
build(1,n,1);
while(q--){
int l,r; scanf("%d%d",&l,&r);
int ll=n-r+1,rr=n-l+1;
node x=query(1,n,1,ll,rr);
int ans=x.a[0][4];
if(ans==maxn) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}