BZOJ 4631 踩气球

Description

Solution

这题的解法非常巧妙啊~
我们发现只有一个箱子中的气球全部被踩完后, 才可能对答案产生贡献, 因此用数组维护每个箱子中剩余的气球个数.
考虑每次可能对处于哪些区间的熊孩子产生影响: 我们设当前箱子为\(p\), 通过链表查找到最小的\(l\)满足\([l, p]\)区间的所有箱子都不含有气球, 以及最大的\(r\)满足\([p, r]\)区间中的所有箱子都不含有气球. 则会变高兴的熊孩子满足其对应区间的左端点在\([l, p]\)中且右端点在\([p, r]\)中. 我们用一颗可持久化线段树来维护每个位置\(p\)有哪些熊孩子满足\(l \le p\), 且将其对应的\(r\)存入线段树中. 每次操作后, 在线段树中容斥即可.

#include <cstdio>
#include <cctype>
#include <algorithm>

namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1;
        char c;
        while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
        while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int N = (int)1e5, M = (int)1e5;
int n, m;
struct bearChild
{
    int L, R;
    inline int friend operator <(bearChild a, bearChild b)
    {
        return a.L == b.L ? a.R < b.R : a.L < b.L;
    }
}chd[M];
struct segmentTrees
{
    struct node
    {
        node *suc[2];
        int cnt;
        inline node() {cnt = 0; suc[0] = suc[1] = NULL;}
    };
    node *rt[N + 1];
    node* build(int L, int R)
    {
        node *u = new node;
        if(L == R) return u;
        u->suc[0] = build(L, L + R >> 1); u->suc[1] = build((L + R >> 1) + 1, R);
        return u;
    }
    inline void build() {rt[0] = build(1, n);}
    inline void newTree(int id) {rt[id] = rt[id - 1];}
    inline node* insert(node *_u, node *u, int L, int R, int pos)
    {
        if(u == _u) u = new node, *u = *_u;
        ++ u->cnt;
        if(L == R) return u;
        if(pos <= L + R >> 1) u->suc[0] = insert(_u->suc[0], u->suc[0], L, L + R >> 1, pos);
        else u->suc[1] = insert(_u->suc[1], u->suc[1], (L + R >> 1) + 1, R, pos);
        return u; 
    }
    inline void insert(int id, int pos)
    {
        rt[id] = insert(rt[id - 1], rt[id], 1, n, pos);
    }
    int query(node *u, int curL, int curR, int L, int R)
    {
        if(curL >= L && curR <= R) return u->cnt;
        int mid = curL + curR >> 1;
        int res = 0;
        if(L <= mid) res += query(u->suc[0], curL, mid, L, R);
        if(R > mid) res += query(u->suc[1], mid + 1, curR, L, R);
        return res;
    }
    inline int query(int id, int L, int R)
    {
        return query(rt[id], 1, n, L, R);
    }
}seg;
int main()
{
    
    #ifndef ONLINE_JUDGE
    
    freopen("balloon.in", "r", stdin);
    freopen("balloon.out", "w", stdout);
    
    #endif
    
    using namespace Zeonfai;
    n = getInt(), m = getInt();
    static int a[N + 1];
    for(int i = 1; i <= n; ++ i) a[i] = getInt();
    for(int i = 0; i < m; ++ i) chd[i].L = getInt(), chd[i].R = getInt();
    std::sort(chd, chd + m);
    seg.build();
    for(int i = 1, p = 0; i <= n; ++ i)
    {
        seg.newTree(i);
        for(; p < m && chd[p].L == i; ++ p) seg.insert(i, chd[p].R);
    }
    static int pre[N + 1], nxt[N + 1];
    for(int i = 1; i <= n; ++ i) pre[i] = i - 1, nxt[i] = i + 1;
    pre[1] = nxt[n] = -1;
    int q = getInt(), ans = 0;
    for(int i = 0; i < q; ++ i)
    {
        int pos = (getInt() + ans - 1) % n + 1; -- a[pos];
        if(! a[pos])
        {
            ans += seg.query(pos, pos, ~ nxt[pos] ? nxt[pos] - 1 : n);
            if(~ pre[pos]) ans -= seg.query(pre[pos], pos, ~ nxt[pos] ? nxt[pos] - 1 : n);
            if(~ nxt[pos]) pre[nxt[pos]] = pre[pos];
            if(~ pre[pos]) nxt[pre[pos]] = nxt[pos];
        }
        printf("%d\n", ans);
    }
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/7348178.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值