CF750E New Year and Old Subsequence(dp+线段树分治+矩阵)

我们考虑5种状态的矩阵:
0–什么都没出现
1–出现了2
2–出现了20
3–出现了201
4–出现了2017
重定义 加法为取min,乘法为 加,每个点根据字符可以得到一个初始矩阵
那么答案就是l~r的矩阵的乘积。因为矩阵乘法满足结合律,因此我们可以用线段树分治来快速求区间矩阵乘积。
复杂度 O ( ( n + m ) l o g n 5 2 )
另外这样可以加强到带单点修改,还可以不只有删除操作,还可以有花给定代价修改一个字符,求最小代价。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
char s[N];
inline void get_S(){
    char ch=gc();int tot=0;
    while(ch<'0'||ch>'9') ch=gc();
    while(ch>='0'&&ch<='9') s[++tot]=ch,ch=gc();
}
int n,m;
struct node{
    int a[5][5];//0-- null 1-- 2 2-- 20 3-- 201 4--2017
    int* operator[](int x){return a[x];}
    inline void init(){memset(a,inf,sizeof(a));}
    friend node operator+(node a,node b){
        node res;res.init();
        for(int i=0;i<5;++i)
            for(int j=0;j<5;++j)
                for(int k=0;k<5;++k)
                    res[i][j]=min(res[i][j],a[i][k]+b[k][j]);
        return res;
    }
}tr[N<<2];
inline void build(int p,int l,int r){
    if(l==r){
        tr[p].init();
        for(int i=0;i<5;++i) tr[p][i][i]=0;
        if(s[l]=='2'){
            tr[p][0][1]=0;
            tr[p][0][0]=1;return;
        }if(s[l]=='0'){
            tr[p][1][2]=0;
            tr[p][1][1]=1;return;
        }if(s[l]=='1'){
            tr[p][2][3]=0;
            tr[p][2][2]=1;return;
        }if(s[l]=='6'){
            tr[p][3][3]=1;
            tr[p][4][4]=1;return;
        }if(s[l]=='7'){
            tr[p][3][4]=0;
            tr[p][3][3]=0;return;
        }return;
    }int mid=l+r>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
    tr[p]=tr[p<<1]+tr[p<<1|1];
}
inline node ask(int p,int l,int r,int x,int y){
    if(x==l&&r==y) return tr[p];int mid=l+r>>1;
    if(y<=mid) return ask(p<<1,l,mid,x,y);
    if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
    return ask(p<<1,l,mid,x,mid)+ask(p<<1|1,mid+1,r,mid+1,y);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();
    get_S();build(1,1,n);
    while(m--){
        int x=read(),y=read();
        int res=ask(1,1,n,x,y).a[0][4];
        if(res==inf) puts("-1");
        else printf("%d\n",res);
    }return 0;
}

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80721963