【洛谷4215】踩气球(线段树)

题目:

洛谷4215

分析:

感觉思路有点像线段树分治?

把所有区间插到线段树上。我一开始的想法是修改时给树上一条链上包含的所有熊孩子的值都减 1 1 1,然后发现这个单次最坏是 O ( m ) O(m) O(m)的,gg

可以记录每个熊孩子被分成了多少个非零的区间。修改时如果某个区间变成 0 0 0了,那么就给包含该区间的熊孩子的区间数减 1 1 1,这样均摊最多有 m log ⁡ m m\log m mlogm个区间,总复杂度 O ( q log ⁡ n + m log ⁡ n ) O(q\log n+m\log n) O(qlogn+mlogn)。和暴力相比,这样做相当于把每个熊孩子最多分的段数从 n n n(每个位置一段)变成了 log ⁡ n \log n logn

代码:

#include 
#include 
#include 
#include 
using namespace std;
#define _ 0

namespace zyt
{
    template<typename T>
    inline void read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != '-' && !isdigit(c));
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    const int N = 1e5 + 10, B = 17;
    struct edge
    {
        int to, next;
    }e[N * B];
    int n, m, q, arr[N], seg[N], head[1 << (B + 1) | 11], ecnt, ans;
    inline void add(const int a, const int b)
    {
        e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
    }
    namespace Segment_Tree
    {
        struct node
        {
            int sum;
        }tree[1 << (B + 1) | 11];
        inline void update(const int rot)
        {
            tree[rot].sum = tree[rot << 1].sum + tree[rot << 1 | 1].sum;
        }
        void build(const int rot, const int lt, const int rt)
        {
            head[rot] = -1;
            if (lt == rt)
            {
                tree[rot].sum = arr[lt];
                return;
            }
            int mid = (lt + rt) >> 1;
            build(rot << 1, lt, mid);
            build(rot << 1 | 1, mid + 1, rt);
            update(rot);
        }
        void insert(const int rot, const int lt, const int rt, const int ls, const int rs, const int id)
        {
            if (ls <= lt && rt <= rs)
            {
                if (tree[rot].sum)
                    ++seg[id];
                add(rot, id);
                return;
            }
            int mid = (lt + rt) >> 1;
            if (ls <= mid)
                insert(rot << 1, lt, mid, ls, rs, id);
            if (rs > mid)
                insert(rot << 1 | 1, mid + 1, rt, ls, rs, id);
        }
        void change(const int rot, const int lt, const int rt, const int pos)
        {
            if (lt == rt)
                --tree[rot].sum;
            else
            {
                int mid = (lt + rt) >> 1;
                if (pos <= mid)
                    change(rot << 1, lt, mid, pos);
                else
                    change(rot << 1 | 1, mid + 1, rt, pos);
                update(rot);
            }
            if (!tree[rot].sum)
                for (int i = head[rot]; ~i; i = e[i].next)
                    if (!--seg[e[i].to])
                        ++ans;
        }
    }
    int work()
    {
        using namespace Segment_Tree;
        read(n), read(m);
        for (int i = 1; i <= n; i++)
            read(arr[i]);
        build(1, 1, n);
        for (int i = 1; i <= m; i++)
        {
            int l, r;
            read(l), read(r);
            insert(1, 1, n, l, r, i);
            if (!seg[i])
                ++ans;
        }
        read(q);
        int lastans = 0;
        while (q--)
        {
            int x;
            read(x);
            x = (x + lastans - 1) % n + 1;
            change(1, 1, n, x);
            write(lastans = ans), putchar('\n');
        }
        return ~~(0^_^0);
    }
}
int main()
{
    return zyt::work();
}

你可能感兴趣的:(数据结构)