洛谷·踩氣球

初見安~這裏是傳送門:洛谷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——

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