【SDUT OJ 2610】 Boring Counting(主席树)

【SDUT OJ 2610】 Boring Counting(主席树)

Boring Counting

Time Limit: 3000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries,
for each query, please tell us among [L, R], how many Pi is not less than A and not greater than B( L<= i <= R). In other words, your task is to count the number of Pi (L <= i <= R,  A <= Pi <= B).

输入

In the first line there is an integer T (1 < T <= 50), indicates the number of test cases.

For each case, the first line contains two numbers N and M (1 <= N, M <= 50000), the size of sequence P, the number of queries. The second line contains N numbers Pi(1 <= Pi <= 10^9), the number sequence P. Then there are M lines, each line contains four
number L, R, A, B(1 <= L, R <= n, 1 <= A, B <= 10^9)

输出

For each case, at first output a line ‘Case #c:’, c is the case number start from 1. Then for each query output a line contains the answer.

示例输入

1
13 5
6 9 5 2 3 6 8 7 3 2 5 1 4
1 13 1 10
1 13 3 6
3 6 3 6
2 8 2 8
1 9 1 9

示例输出

Case #1:
13
7
3
6
9

提示

来源

2013年山东省第四届ACM大学生程序设计竞赛

示例程序

主席树——从刚进ACM没几个月就经常看到各大ACM群里这个词时不时冒个泡,而且是近几年新发现的一种数据结构。

从当时对这个东西就一直是很崇拜的姿态(90°仰望

昨天听Hongfeng巨一讲,立马醍醐灌顶茅塞顿开……

首先 主席树使用来干什么——求解静态区间第k大

主席树实现原理——用空间换时间,对于n个数,从左到右依次记录,这样得到n棵线段树构成主席树。

线段树中区间的含义——将所有n个数排序离散化后,从小到大各个数的编号构成的区间。

线段树的意义——求加入到当前数为止,所有区间中已经出现了的数的个数。

线段树做好后,会发现每次建一棵树复杂度会很高很高。

同时你会发现,加入第i个数的时候,此时需要建立新线段树,同时线段树是二叉结构,第i个线段树其实可以由第i-1棵线段树推出,这样其实改变了的节点只有log2(n)个。

即为从根到叶子的一条链。这样对于重复(未改变)的节点,直接指向i-1棵树相应位置的节点即可。

这样当求解从左到右边第i个数间大于x的数,遍历第i个线段树,找到所有右界小于等于x的区间,统计出累加和即可

当求[L,R]区间内大于x的数,遍历第R个线段树,用答案减去遍历第L个线段树的答案即可

当求[L,R]区间内第k大的数,通过补的方法,用右区间不断补足k,当较大的数出现够k个时,即得到答案

对于此题 求[L,R]区间内[A,B]范围内的数的个数。遍历直到区间[l,r] 满足 p[l] >= A, p[r] <= B即可(p为离散化后的数存放的数组)

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread() freopen("data1.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Node
{
	//当前区间内出现的数的个数
	int v;
	//左节点  右节点编号
	int l,r;
};

Node nd[2333333];
//原数        离散化
int num[55555],qt[55555];
//n棵线段树的根节点
int tm[55555];
int tp,n,tot;

//建立新节点 返回下标
int NewNode(int x)
{
	nd[tp].v = x;
	nd[tp].l = nd[tp].r = -1;
	return tp++;
}

//初始化第一棵线段树(所有区间均为0)
int init(int l,int r)
{
	if(l == r) return NewNode(0);
	int mid = (l+r)>>1;
	int rt = NewNode(0);
	nd[rt].l = init(l,mid);
	nd[rt].r = init(mid+1,r);
	return rt;
}

//建立线段树
int Build(int root,int l,int r,int x)
{
	//当前区间中新出现一个数——x
	int rt = NewNode(nd[root].v+1);
	//为叶子节点时跳出
	if(l == r)
		return rt;
	int mid = (l+r)>>1;
	if(x <= qt[mid])
	{
		nd[rt].r = nd[root].r;
		nd[rt].l = Build(nd[root].l,l,mid,x);
	}
	else
	{
		nd[rt].l = nd[root].l;
		nd[rt].r = Build(nd[root].r,mid+1,r,x);
	}
	return rt;
}

//查询某[l,r]内[low,high]范围内数的个数
int Search(int root,int l,int r,int low,int high)
{
	//注意特判一下 否则会停不下来……
	if(qt[l] > high || qt[r] < low) return 0;
	if(qt[l] >= low && qt[r] <= high) return nd[root].v;
	int mid = (l+r)>>1;

	if(qt[mid] >= high) return Search(nd[root].l,l,mid,low,high);
	else if(qt[mid+1] <= low) return Search(nd[root].r,mid+1,r,low,high);
	else return Search(nd[root].l,l,mid,low,high)+Search(nd[root].r,mid+1,r,low,high);
}

int main()
{
	//fread();
	//fwrite();

	int t,q;
	int st,en,low,high;
	scanf("%d",&t);
	for(int z = 1; z <= t; ++z)
	{
		tp = 0;
		scanf("%d%d",&n,&q);
		for(int i = 1; i <= n; ++i)
		{
			scanf("%d",&num[i]);
			qt[i] = num[i];
		}

		sort(qt+1,qt+n+1);
		tot = unique(qt+1,qt+n+1)-qt-1;
		tm[0] = init(1,tot);
		for(int i = 1; i <= n; ++i)
			tm[i] = Build(tm[i-1],1,tot,num[i]);

		printf("Case #%d:\n",z);
		while(q--)
		{
			scanf("%d%d%d%d",&st,&en,&low,&high);
			printf("%d\n",Search(tm[en],1,tot,low,high)-Search(tm[st-1],1,tot,low,high));
		}
	}

	return 0;
}

时间: 2024-10-10 00:25:00

【SDUT OJ 2610】 Boring Counting(主席树)的相关文章

sdut 2610:Boring Counting(第四届山东省省赛原题,划分树 + 二分)

Boring Counting Time Limit: 3000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries, for each quer

SDUT OJ 3045 迷之图论 (树的直径)

题目地址:SDUT OJ 3045 这题比赛的时候想的差不多..但是总是觉得不对..写了一次就没再写,然后删了..当时没想到的是第二次求出来的就是最长链..当时想到的两次bfs找最大值(这一种方法其实结果也对..TAT..),还有找到点后在回溯减去重点等等..但总觉得好像都不太对...赛后才知道这题原来是树的直径.....牡丹江区域现场赛的时候遇到过,不过赛后也没看... 找树的直径的方法其实就是先任取一点进行bfs,找到最远的一点,这时最远的一点肯定是最长链端点之一,然后再从这一最远点开始bf

HDU 4358 Boring counting(树状数组)

题意:  给定一棵树,每个节点有一个点权,然后有一些询问,求以某个点为根的子树中有多少的数出现了恰好k次. 思路: 首先dfs一次将树形结构转化成线性结构,利用时间戳记录下以结点u为根的子树在数组中的开始位置和结束位置. 那么我们将所有查询记录下来离线来做,将所有的查询按右端点升序排序. 考虑用树状数组来做这道题,每个位置记录当前从1到当前位置有多少数出现了恰好k次. 从头遍历一遍数组,map离散化记录每个值出现的位置,对于每个位置,如果值出现的次数t大于k,那么在将第t-k次出现的位置加一

13年山东省赛 Boring Counting(离线树状数组or主席树+二分or划分树+二分)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud 2224: Boring Counting Time Limit: 3 Sec  Memory Limit: 128 MB Description In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence.

UPC 2224 Boring Counting (离线线段树,统计区间[l,r]之间大小在[A,B]中的数的个数)

题目链接:http://acm.upc.edu.cn/problem.php?id=2224 题意:给出n个数pi,和m个查询,每个查询给出l,r,a,b,让你求在区间l~r之间的pi的个数(A<=pi<=B,l<=i<=r). 参考链接:http://www.cnblogs.com/zj62/p/3558967.html #include <iostream> #include <cstdio> #include <cstring> #incl

bzoj3489: A simple rmq problem (主席树)

//========================== 蒟蒻Macaulish:http://www.cnblogs.com/Macaulish/  转载要声明! //========================== 说好的“因为是OJ上的题,就简单点好了.”呢? 一开始看不懂,不会写. 然后跪了一个晚上决定看云的题解&……似乎是主席树套主席树!吓傻,还开了40000000的数组.然后一交tle…… 然后p是不可能玩常数的. 找不到其他做法. 然后找到了神牛dwjshift,好心地提供了题

SDUT OJ -2892 A

A Time Limit: 60ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给出n(1<= n && n <= 2*10^6)个字符串,每个字符串只包含小写英文字母,且最多有五个.问这n个字符串中出现次数最多的有多少个. 输入 单组输入.第一行输入一个数字n,接下来n行,每行包含一个字符串. 输出 输出一个数字代表答案. 示例输入 5 aba abb w aba z 示例输出 2 提示 字段树,模板题 #include <iost

sdut2610---Boring Counting(离线+树状数组+离散化)

Boring Counting Time Limit: 3000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries, for each query,

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include