2019ICPC南昌网络赛 Hello 2019(线段树维护矩阵)

A digital string is "good": when it contains a subsequence 91029102 and does not contain a subsequence 81028102.

The bad value of a string is defined as how many characters are to remove at least, so that the string satisfies the "good" property. Output -1 if the string cannot satisfy the "good" property by removing some characters (0 or maybe more).

Input

The first line contains two integers n, Qn,Q(1\leq n,Q\leq2*10^5)(1≤n,Q≤2∗105). Where nn is the length of the string and QQ is the number of queries.

The second line contains a string ss that consists entirely of decimal numbers.

The next QQ line, each line contains two integers l, rl,r(1\leq l\leq r\leq n)(1≤l≤r≤n), denoting a query.

Output

For each query, output an answer which is the bad value of the substring s_ls_{l+1} \cdots s_rsl​sl+1​⋯sr​ from ss.

样例输入复制

8 3
88988102
1 8
2 8
1 7

样例输出复制

4
3
-1

 

题意:

给了一个长度为N的字符串,Q次询问,要求区间[L,R]最少要删除多少个字符保证字符串只会出现像“9102”这样的序列,不会出现“8102”这样的序列。

 

思路:

这道题是CF 750E原题,虽然是原题,但是思路却从没见过。非常值得记录下来。

我们设置了5个状态。

0表示初始状态,1表示只有“2”,2表示只有“20”,3表示只有“201”,4表示只有“2019”这四个状态。

然后构造一个5x5的矩阵,dp[i][j]表示的是从i状态到j状态的最小花费。

所以最后只要知道查询区间的dp[0][4]的值是多少就行了。

而这件事可以用线段树来做。

而在这里面的矩阵运算是把乘法改成了加法取min,这样类似floyd求最短路。

而对于矩阵的每一个状态的转移,在构建线段树的时候处理好就行了。

具体处理看代码就会明白了。

#include 
using namespace std;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
char a[maxn];
struct node
{
    int dp[5][5];
    void init(){
        memset(dp,inf,sizeof(dp));
        for(int i=0;i<5;i++) dp[i][i]=0;
    }
    friend node operator * (node a,node b){
        node tmp;//tmp.init();
        memset(tmp.dp,inf,sizeof(tmp.dp));
        for(int i=0;i<5;i++){
            for(int j=0;j<5;j++){
                for(int k=0;k<5;k++){
                    tmp.dp[i][j]=min(tmp.dp[i][j],a.dp[i][k]+b.dp[k][j]);
                }
            }
        }
        return tmp;
    }
}tr[maxn<<2];
void build(int i,int l,int r)
{
    if(l==r)
    {
        tr[i].init();
        int c=a[l]-'0';
        if(c==2) tr[i].dp[0][0]=1,tr[i].dp[0][1]=0;
        if(c==0) tr[i].dp[1][1]=1,tr[i].dp[1][2]=0;
        if(c==1) tr[i].dp[2][2]=1,tr[i].dp[2][3]=0;
        if(c==9) tr[i].dp[3][3]=1,tr[i].dp[3][4]=0;
        if(c==8) tr[i].dp[3][3]=1,tr[i].dp[4][4]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(2*i,l,mid);
    build(2*i+1,mid+1,r);
    tr[i]=tr[2*i]*tr[2*i+1];
}
node query(int i,int l,int r,int x,int y)
{
    node ans;
    ans.init();
    if(x<=l&&r<=y) return tr[i];
    int mid=(l+r)>>1;
    if(x<=mid) ans=ans*query(2*i,l,mid,x,y);
    if(y>mid) ans=ans*query(2*i+1,mid+1,r,x,y);
    return ans;
}
int main()
{
    int n,q,l,r;
    scanf("%d%d",&n,&q);
    scanf("%s",a+1);
    reverse(a+1,a+1+n);
    build(1,1,n);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&l,&r);
        l=n+1-l;
        r=n+1-r;
        int ans=query(1,1,n,r,l).dp[0][4];
        printf("%d\n",ans==inf?-1:ans);
    }
    return 0;
}

 

你可能感兴趣的:(线段树)