zoukankan      html  css  js  c++  java
  • 【bzoj4631】踩气球 线段树

    题目描述

    给出一个长度为 $n$ 的序列,序列中每一个数都是正整数。现在给出 $m$ 个指定区间以及 $q$ 次操作,每次操作将某个位置的数-1(最多减到0),并询问有多少个指定区间的区间和为0。强制在线。

    输入

    第一行包含两个正整数N和M,分别表示盒子和熊孩子的个数。
    第二行包含N个正整数Ai( 1 < = Ai < = 10^5),表示每个盒子里气球的数量。
    以下M行每行包含两个正整数Li, Ri( 1 < = Li < = Ri < = N),分别表示每一个熊孩子指定的区间。
    以下一行包含一个正整数Q,表示 SHUXK 操作的次数。
    以下Q行每行包含一个正整数X,表示这次操作是从第X个盒子里拿气球。为了体现在线,我们对输入的X进行了加密。
    假设输入的正整数是x',那么真正的X = (x' + Lastans − 1)Mod N + 1。其中Lastans为上一次询问的答案。对于第一个询问, Lastans = 0。
    输入数据保证1 < = x' < = 10^9, 且第X个盒子中有尚未被踩爆的气球。
    N < = 10^5 ,M < = 10^5 ,Q < = 10^5

    输出

    包含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


    题解

    线段树

    考虑朴素暴力:每次操作一个位置,如果该数减到了0,则将所有包含该位置的区间的区间长度-1。当某个区间的区间长度减到0时则答案+1。

    那么如何优化这个暴力呢?线段树。

    一个区间在线段树上对应着至多log个点,记录这个点数。我们对每一个点开一个vector,如果一个区间对应着这个点则加入到这个vector中。

    维护区间和,对于修改操作将路径上的点的区间和-1。如果一个点的区间和减到0,则将其vector中对应的区间的点数-1,区间对应点数减到0则答案+1。

    时间复杂度 $O(nlog n)$

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define N 100010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    typedef long long ll;
    vector<int> v[N << 2];
    ll sum[N << 2];
    int c[N] , ans;
    inline void pushup(int x)
    {
    	sum[x] = sum[x << 1] + sum[x << 1 | 1];
    }
    void build(int l , int r , int x)
    {
    	if(l == r)
    	{
    		scanf("%lld" , &sum[x]);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(lson) , build(rson);
    	pushup(x);
    }
    void init(int b , int e , int id , int l , int r , int x)
    {
    	if(b <= l && r <= e)
    	{
    		v[x].push_back(id) , c[id] ++ ;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(b <= mid) init(b , e , id , lson);
    	if(e > mid) init(b , e , id , rson);
    }
    void solve(int p , int l , int r , int x)
    {
    	sum[x] -- ;
    	if(!sum[x])
    	{
    		vector<int>::iterator i;
    		for(i = v[x].begin() ; i != v[x].end() ; i ++ )
    		{
    			c[*i] -- ;
    			if(!c[*i]) ans ++ ;
    		}
    	}
    	if(l == r) return;
    	int mid = (l + r) >> 1;
    	if(p <= mid) solve(p , lson);
    	else solve(p , rson);
    }
    int main()
    {
    	int n , m , q , i , x , y;
    	scanf("%d%d" , &n , &m);
    	build(1 , n , 1);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x , &y) , init(x , y , i , 1 , n , 1);
    	scanf("%d" , &q);
    	while(q -- )
    	{
    		scanf("%d" , &x);
    		solve((x + ans - 1) % n + 1 , 1 , n , 1);
    		printf("%d
    " , ans);
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    【JAVA、C++】LeetCode 005 Longest Palindromic Substring
    【JAVA、C++】LeetCode 004 Median of Two Sorted Arrays
    【JAVA、C++】LeetCode 003 Longest Substring Without Repeating Characters
    【JAVA、C++】LeetCode 002 Add Two Numbers
    【JAVA、C++】LeetCode 001 Two Sum
    Linux的文件管理
    Ubuntu及Windows ADB设备no permissions的解决方案
    4-[多进程]-互斥锁、Queue队列、生产者消费者
    3 [多进程]-开启多进程
    2-[多进程]-进程理论
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8026440.html
Copyright © 2011-2022 走看看