NC19469 01串

科技资讯 投稿 6400 0 评论

NC19469 01串

题目

题目描述

We were burning on the edge of something beautiful
Something beautiful
Selling a dream
Smoke and mirrors keep us waiting on a miracle
On a miracle
Say go through the darkest of days
Heaven's a heartbreak away
Never let you go
Never let me down
OH it's been a hell of a ride
Driving the edge of a knife
Never let you go
Never let me down
Bieber拥有一个长度为n的01 串,他每次会选出这个串的一个子串作为曲谱唱歌,考虑该子串从左往右读所组成的二进制数P。 Bieber每一秒歌唱可以让P增加或减少 2 的 k次方(k由Bieber选定),但必须保证任意时刻其P大于等于0。
Bieber 是一位追求效率的人 每次Bieber都想知道他歌唱的最少时间将这个数P变成0。
Bieber 正和 一位DJ合作,他随时可能修改串上的一个字符。

输入描述

第二行一个长度为n的字符串s
第三行一个数 t 表示 询问 + 修改总次数
以下 t 行,每行格式如下
第一个数 1 <= type <= 2 表示 类型
Type = 1 表示是一次 询问 接下来两个数 l , r 表示询问的区间。
否则 表示一次修改 接下来两个数x,y 表示把 s[x] 改为y.
n <= 3e5, t <= 3e5

输出描述

示例1

输入

4
1101
1
1 1 4

输出

3

题解

知识点:动态dp,区间dp,线段树。

设 \(f_{l,r,i,j}\ 代表考虑区间 \([l,r]\,左端点状态为 \(i(0/1\ (是否向高位进位),右端点状态为 \(j(0/1\ (是否从低位进位)时的操作次数最小值。

因此,状态转移方程为:

\[\begin{aligned} f_{l,r,i,j} = \min(f_{l,mid,i,0} + f_{mid+1,r,0,j},f_{l,mid,i,1} + f_{mid+1,r,1,j} \end{aligned} \]

但是现在要求能够修改,那就必须使用线段树维护区间的dp信息了,也就是动态dp,每个线段树区间 \([l,r]\ 维护一个矩阵 \(f[0/1][0/1]\ 即可。维护信息的过程是十分显然的,因为此区间dp和分割点无关,就直接按照线段树的修改合并查询就做完了。

时间复杂度 \(O((n+m\log n\

空间复杂度 \(O(n\

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct T {
    array<array<int, 2>, 2> f = { (int1e9,(int1e9,(int1e9,(int1e9 };
    friend T operator+(const T &a, const T &b {
        // 这里用特殊值判断无效区间
        // 当然,也可以在递归的时候直接避免掉无效区间,而不是在合并时才判断
        if (a.f[0][0] == 1e9 return b;
        if (b.f[0][0] == 1e9 return a;
        auto x = T(;
        //* 广义矩阵乘法(乘改加,加改取最大值)
        for (auto i : { 0,1 }
            for (auto j : { 0,1 }
                for (auto k : { 0,1 }
                    x.f[i][j] = min(x.f[i][j], a.f[i][k] + b.f[k][j];
        return x;
    }
};

struct F {
    bool upd;
    T operator((const T &x {
        return{
            upd,1,
            1,!upd
        };
    }
};

template<class T, class F>
class SegmentTree {
    int n;
    vector<T> node;

    void update(int rt, int l, int r, int x, F f {
        if (r < x || x < l return;
        if (l == r return node[rt] = f(node[rt], void(;
        int mid = l + r >> 1;
        update(rt << 1, l, mid, x, f;
        update(rt << 1 | 1, mid + 1, r, x, f;
        node[rt] = node[rt << 1] + node[rt << 1 | 1];
    }

    T query(int rt, int l, int r, int x, int y {
        if (r < x || y < l return T(;
        if (x <= l && r <= y return node[rt];
        int mid = l + r >> 1;
        return query(rt << 1, l, mid, x, y + query(rt << 1 | 1, mid + 1, r, x, y;
    }

public:
    SegmentTree(int _n = 0 { init(_n; }
    SegmentTree(const vector<T> &src { init(src; }

    void init(int _n {
        n = _n;
        node.assign(n << 2, T(;
    }
    void init(const vector<T> &src {
        assert(src.size( >= 2;
        init(src.size( - 1;
        function<void(int, int, int> build = [&](int rt, int l, int r {
            if (l == r return node[rt] = src[l], void(;
            int mid = l + r >> 1;
            build(rt << 1, l, mid;
            build(rt << 1 | 1, mid + 1, r;
            node[rt] = node[rt << 1] + node[rt << 1 | 1];
        };
        build(1, 1, n;
    }

    void update(int x, F f { update(1, 1, n, x, f; }

    T query(int x, int y { return query(1, 1, n, x, y; }
};

int main( {
    std::ios::sync_with_stdio(0, cin.tie(0, cout.tie(0;
    int n;
    cin >> n;
    vector<T> a(n + 1;
    for (int i = 1;i <= n;i++ {
        char x;
        cin >> x;
        a[i] = {
            x == '1',1,
            1,x != '1'
        };
    }
    SegmentTree<T, F> sgt(a;
    int m;
    cin >> m;
    while (m-- {
        int op;
        cin >> op;
        if (op == 1 {
            int l, r;
            cin >> l >> r;
            cout << sgt.query(l, r.f[0][0] << '\n';
        }
        else {
            int x;
            bool val;
            cin >> x >> val;
            sgt.update(x, { val };
        }
    }
    return 0;
}

编程笔记 » NC19469 01串

赞同 (34) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽