洛谷·踩气球

初见安~这里是传送门:洛谷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——

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章