初见安~这里是传送门:洛谷P4215
题目描述
六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着A_iAi个气球。 SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆。 这M个熊孩子每个人都指定了一个盒子区间[L_iLi,R_iRi]。 如果某一个时刻,一个熊孩子发现自己选定的盒子区间[L_iLi,R_iRi]中的所有气球都已经被踩爆了,他就会非常高兴(显然之后他一直会很高兴)。 为了不辜负将自己的任务强行塞给 SHUXK 的那个人的期望, SHUXK 想向你询问: 他每次操作过后会有多少个熊孩子很高兴。
输入格式:
第一行包含两个正整数N和M,分别表示盒子和熊孩子的个数。 第二行包含N个正整数Ai( 1<=A_iAi<=10^5105),表示每个盒子里气球的数量。 以下M行每行包含两个正整数Li, Ri( 1<=L_iLi<=R_iRi<=N),分别表示每一个熊孩子指定的区间。 以下一行包含一个正整数Q,表示 SHUXK 操作的次数。 以下Q行每行包含一个正整数X,表示这次操作是从第X个盒子里拿气球。为了体现在线,我们对输入的X进行了加密。 假设输入的正整数是\hat{x}x^,那么真正的X=(\hat{x}x^+Lastans−1)Mod N +1。其中Lastans为上一次询问的答案。对于第一个询问, Lastans=0。 输入数据保证1<=\hat{x}x^<=10^9109, 且第X个盒子中有尚未被踩爆的气球。 N<=10^5105,M<=10^5105,Q<=10^5105
输出格式:
包含Q行,每行输出一个整数,表示 SHUXK 一次操作后询问的答案。答案的顺序应与输入数据的顺序保持一致。
输入样例:
5 3
1 1 1 1 1
5 5
2 2
1 3
5
4
2
5
2
3
输出样例:
0
1
1
2
3
题解
首先 看标签就知道 这道题
只能用 线段树 来做。但是很明显的有一个问题——我们正常构建线段树,正常修改叶子结点的值,怎么判定有没有涉及到某个甚至是某些熊孩子的区间呢?如果是把他们的区间全部存起来,在修改前先遍历一下,那就很容易TLE了,而且代码量不小。所以——我们索性把熊孩子拆开 ,标记他们所指的区间,当那个区间的值为0时,这个熊孩子就happy了。所以牵扯出来的又一个问题——一个熊孩子的区间可能会跨越多个我们线段树上的区间,线段树上的一个区间也可能被多个熊孩子盯上,所以——熊孩子的区间所跨越的几个区间我们可以挨个标记,为了避免重复及开头说的遍历,我们就在读入熊孩子的区间后做标记,并统计这个孩子涉及到了多少个线段树上的区间;而树上每个区间我们都开一个struct,里面存一个vector来记录这个区间被哪些熊孩子盯上了,当这个区间的值为0时,这些熊孩子就可以挨个happy了。当然也不一定——由于一个孩子多个区间,统计时需要开一个cnt数组来统计各个熊孩子的区间跨越了多少个区间,这样只要其中一个区间的值为0了,对应的 cnt - - 即可。
大致思路都如上啦~下面是代码及详解:
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int m, n, a[maxn], x, y, q, lastans = 0, cnt[maxn];
struct node
{
vector<int> boy;
int ball;
}t[maxn << 2];
void build(int p, int l, int r)//建树,基本操作
{
if(l == r)
{
t[p].ball = a[l];
return;
}
int mid = l + r >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
t[p].ball = t[p * 2].ball + t[p * 2 + 1].ball;
}
void sigh(int p, int l, int r, int ls, int rs, int x)//sign标记熊孩子的区间
//由于结构体里没有存l 和 r,所以需要传值。
{
if(ls <= l && r <= rs)//完全覆盖
{
t[p].boy.push_back(x);
cnt[x]++;//统计覆盖区间数
return;
}
int mid = l + r >> 1;
if(ls <= mid) sigh(p * 2, l, mid, ls, rs, x);
if(rs > mid) sigh(p * 2 + 1, mid + 1, r, ls, rs, x);
}
void change(int p, int l, int r, int x)
{
if(l == r)//特判到了叶子结点就可以修改值了
{
t[p].ball--;//爆一个气球
if(!t[p].ball && t[p].boy.size())//有熊孩子盯上才操作,当然这一步也可以略(吧
{
for(int i = 0; i < t[p].boy.size(); i++)
{
cnt[t[p].boy[i]]--;
if(!cnt[t[p].boy[i]]) lastans++;
}
}
return;//不必再操作
}
int mid = l + r >> 1;
if(x <= mid) change(p * 2, l, mid, x);
else change(p * 2 + 1, mid + 1, r, x);
t[p].ball = t[p * 2].ball + t[p * 2 + 1].ball;
if(!t[p].ball && t[p].boy.size())//此处代码和特判里的一样,可能哪里冗长了一点,但好理解。
{
for(int i = 0; i < t[p].boy.size(); i++)
{
cnt[t[p].boy[i]]--;
if(!cnt[t[p].boy[i]]) lastans++;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1, 1, n);
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
sigh(1, 1, n, x, y, i);
}
scanf("%d", &q);
for(int i = 1; i <= q; i++)
{
scanf("%d", &x);
x = (x + lastans - 1) % n + 1;//题目要求强行在线……
change(1, 1, n, x);
printf("%d\n", lastans);//lastans全程不用清,happy的孩子会一直笑到最后。
}
return 0;
}
迎评:)
——End——