BZOJ 4631 踩气球
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);
}
}