题解 修改字符串 DDP基础题

题解 修改01串

题目描述

题目描述数据范围:
在这里插入图片描述

具体做法与心路历程

这道题作为 T 1 T1 T1真心觉得出题人毒瘤好吧是我太菜。看这题目感觉妥妥的贪心,贪心思路都有,且还是对的。可惜只有 50 p t s 50pts 50pts,然后开始优化贪心,搞了个线段树,一码就是 300 + 300+ 300+,结果错了。CYJian线段树贪心A了,还只有200+。考完才知道正解是 D P ! DP! DP!,然而我连想都没想。

具体做法

方法是 D D P DDP DDP。设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示使第 1 1 1 ~ i i i位全部变成 0 0 0,且有没有对下一位进位的方案数, a [ i ] a[i] a[i]表示原字符串的字符。转移为:

  • f [ i ] [ 0 ] = m i n ( f [ i − 1 ] [ 0 ] + 1 − a [ i ] , f [ i − 1 ] [ 1 ] + a [ i ] ) f[i][0]=min(f[i-1][0]+1-a[i],f[i-1][1]+a[i]) f[i][0]=min(f[i1][0]+1a[i],f[i1][1]+a[i]). 第 i − 1 i-1 i1位如果没进位,且第 i i i位为 1 1 1,那么答案+1,否则不加,如果第 i − 1 i-1 i1位进位,且第 i i i位不为 1 1 1,那么答案+1,否则不加。在这些情况中取最小值即可。
  • [ i ] [ 1 ] = m i n ( f [ i − 1 ] [ 0 ] + 2 − a [ i ] , f [ i − 1 ] [ 1 ] + 1 − a [ i ] [i][1]=min(f[i-1][0]+2-a[i],f[i-1][1]+1-a[i] [i][1]=min(f[i1][0]+2a[i],f[i1][1]+1a[i]. 第 i − 1 i-1 i1位没进位,且第 i i i位为 0 0 0,答案+2,为1则+1,第 i − 1 i-1 i1位进位,第 i i i位为 0 0 0则答案+1,否则不加。

这个如果不修改则 O ( n ) O(n) O(n)就做完了,如果修改的话可以考虑 D D P DDP DDP,发现这个式子可以写成广义矩阵的形式,用线段树维护即可。话说我DDP板子题都没打过的蒟蒻这些题倒是做了几道了

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月01日 星期五 22时09分42秒
*******************************/
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

struct Matrix{
    int a[2][2];
    int * operator[](const int i) { return a[i]; }
    const int * operator[](const int i) const { return a[i]; }
    Matrix operator*(const Matrix p)
    {
        Matrix t;
        t[0][0]=min(a[0][0]+p[0][0],a[0][1]+p[1][0]);
        t[0][1]=min(a[0][0]+p[0][1],a[0][1]+p[1][1]);
        t[1][0]=min(a[1][0]+p[0][0],a[1][1]+p[1][0]);
        t[1][1]=min(a[1][0]+p[0][1],a[1][1]+p[1][1]);
        return t;
    }
};

const int maxn=3e5+10;
const Matrix M0={{{0,1},{2,1}}};
const Matrix M1={{{1,2},{1,0}}};
int n,m,a[maxn];
string s;

/*{{{线段树*/

namespace SegmentTree{

    Matrix tr[maxn*4];

    void update(int k)
    {
        tr[k]=tr[k<<1]*tr[k<<1|1];
    }

    void build(int k,int l,int r)
    {
        if(l==r)
        {
            tr[k]=(a[l]?M1:M0);
            return;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
   l     update(k);
    }

    void modify(int k,int l,int r,int pos)
    {
        if(l==r)
        {
            tr[k]=(a[pos]?M1:M0);
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
            modify(k<<1,l,mid,pos);
        else
            modify(k<<1|1,mid+1,r,pos);
        update(k);
        //print(tr[k]);
    }

    Matrix query(int k,int l,int r,int x,int y)
    {
        if(l>=x && r<=y) 
            return tr[k];
        int mid=(l+r)>>1;
        if(y<=mid) return query(k<<1,l,mid,x,y);
        if(x>=mid+1) return query(k<<1|1,mid+1,r,x,y);
        return query(k<<1,l,mid,x,y)*query(k<<1|1,mid+1,r,x,y);
    }

};

/*}}}*/


int main()
{
    //freopen("string.in","r",stdin);
    //freopen("string.out","w",stdout);
    cin>>n;
    s.resize(n+1); s[0]='$';
    scanf("%s",&s[1]);
    cin>>m;
    for(int i=1;i<=n;i++)
        a[i]=s[i]-'0';
    SegmentTree::build(1,1,n);
    while(m--)
    {
        int opt,l,r;
        cin>>opt>>l>>r;
        if(opt==1)
        {
            Matrix ans=SegmentTree::query(1,1,n,l,r);
            printf("%d\n",min(ans[0][0],ans[1][0]+1));
        }
        else
        {
            if(a[l]^r)
            {
                a[l]=r;
                SegmentTree::modify(1,1,n,l);
            }
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值