zoukankan      html  css  js  c++  java
  • 分治维护dp——19南昌网络赛C/cf750E

    南昌网络赛,是cf的原题

    第一次做到这种题,所以认真想了下,每次给一个询问[L,R],要求出这个区间里有2017子序列,但是不能有2016子序列需要删掉的最少元素个数

    首先如果我们之询问一小段区间[L,R]那么显然有一个简单的三维dp可以做,状态0|1|2|3|4表示关键字一个也没有,有2,有21,有201,有2017的情况,dp[i][j]表示从状态i转移到状态j最小需要删除的字符

    那么显然当s[i]=6时,有dp[3][3]=1,dp[4][4]=1

    可以发现,这种状态是很好合并的,对于区间[l,mid]和区间[mid+1,r],设前一半的状态是dp1,后一半的状态是dp2,,两个区间合起来的状态是dp[l][r],那么就有dp1[l][k]+dp2[k][r]=dp[l][r]

    所以我们可以直接用分治来求任意一个区间的所有状态复杂度是O(125/6nlogn)因为时间给的多,所以足够快

    #include<bits/stdc++.h>
    using namespace std;
    #define N 200005
    #define INF 0x3f3f3f3f
    char s[N];
    int n,q;
    
    void reserve(int l,int r){
        int i=l,j=r;
        while(i<j){
            swap(s[i],s[j]);
            ++i,--j;
        }
    }
    
    //状态0表示什么都没有,状态1表示2,状态2表示20,状态3表示201,状态4表示2019,dp[i][j]表示从i->j的代价 
    //因为每段相邻的段状态具有可合并性,想到用线段树分治来维护合并信息,线段树[l,r]维护[l,r]所有状态的代价,合并时类似n^3的区间dp转移 
    struct Node{
        int dp[5][5];
        Node(){
            memset(dp,0x3f,sizeof dp);
        }
    }seg[N<<2];
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1 
    Node merge(Node a,Node b){
        Node res;
        for(int l=0;l<5;l++)
            for(int r=0;r<5;r++)
                for(int k=0;k<5;k++)
                    res.dp[l][r]=min(res.dp[l][r],a.dp[l][k]+b.dp[k][r]);
        return res;
    } 
    void build(int l,int r,int rt){
        if(l==r){
            for(int i=0;i<5;i++)
                seg[rt].dp[i][i]=0;
            if(s[l]=='2'){
                seg[rt].dp[0][0]=1;seg[rt].dp[0][1]=0;
            }else if(s[l]=='0'){
                seg[rt].dp[1][1]=1;seg[rt].dp[1][2]=0;
            }else if(s[l]=='1'){
                seg[rt].dp[2][2]=1;seg[rt].dp[2][3]=0;
            }else if(s[l]=='9'){
                seg[rt].dp[3][3]=1;seg[rt].dp[3][4]=0;
            }else if(s[l]=='8'){
                seg[rt].dp[3][3]=1;seg[rt].dp[4][4]=1;
            }
            return;
        }
        int m=l+r>>1;
        build(lson),build(rson);
        seg[rt]=merge(seg[rt<<1],seg[rt<<1|1]);
    }
    Node query(int L,int R,int l,int r,int rt){
        if(L<=l && R>=r)return seg[rt];
        int m=l+r>>1;
        Node res;
        for(int i=0;i<5;i++)res.dp[i][i]=0;
        if(L<=m)res=merge(res,query(L,R,lson));
        if(R>m)res=merge(res,query(L,R,rson));
        return res;
    }
    
    
    int main(){
        cin>>n>>q;
        scanf("%s",s+1);
        reserve(1,n); 
        build(1,n,1);
        while(q--){
            int L,R;
            scanf("%d%d",&L,&R);
            L=n-L+1;R=n-R+1;
            Node res=query(R,L,1,n,1);
            if(res.dp[0][4]==INF)
                puts("-1");
            else cout<<res.dp[0][4]<<endl;
        }
    }
  • 相关阅读:
    intellij idea 主题更换(换黑底或白底)
    intellij idea 编码设置(乱码问题)
    Intellij idea中maven加载jar包很慢的解决方案.
    mysql一个特殊的条件.字符串除以0的结果.
    tomcat启动报错:Injection of autowired dependencies failed
    给input文本框添加灰色提示文字,三种方法.
    jquery插件开发尝试(二)
    初试jquery插件开发
    jquery.touchslider.min.js的简单使用
    superslide 学习笔记
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11489107.html
Copyright © 2011-2022 走看看