树状数组-例题

例题1 cows



题目描述:

Farmer John‘s cows have discovered that the clover growing along the ridge of the hill (which we can think of as a one-dimensional number line) in his field is particularly good.

Farmer John has N cows (we number the cows from 1 to N). Each
of Farmer John‘s N cows has a range of clover that she particularly
likes (these ranges might overlap). The ranges are defined by a closed
interval [S,E].

But some cows are strong and some are weak. Given two cows: cow
i and cow
j, their favourite clover range is [Si, Ei] and [Sj,
Ej]. If Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj, we say
that cow
i is stronger than cow
j.

For each cow, how many cows are stronger than her? Farmer John needs your help!

输入

The input contains multiple test cases.
For each test case, the first line is an integer N (1 <= N <= 10
5), which is the number of cows. Then come N lines, the i-th of which contains two integers: S and E(0 <= S < E <= 10
5) specifying the start end location respectively of a
range preferred by some cow. Locations are given as distance from the
start of the ridge.

The end of the input contains a single 0.

输出

The input contains multiple test cases.
For each test case, the first line is an integer N (1 <= N <= 10
5), which is the number of cows. Then come N lines, the i-th of which contains two integers: S and E(0 <= S < E <= 10
5) specifying the start end location respectively of a
range preferred by some cow. Locations are given as distance from the
start of the ridge.

The end of the input contains a single 0.

样例输入

3
1 2
0 3
3 4
0

样例输出

1 0 0

提示

Huge input and output,scanf and printf is recommended.



题目意思:

给定n各区间,为S1-Sn和E1-En

对于每一个区间[Si,Ei]求出能够覆盖自身的区间的数量

题目思路:

使用树状数组,排序cow(表示每个区间)数组,按E从大到小排序,那么如果E相等,则排序S从小到大排。

那么排序之后的结果cow就保证能覆盖[i,j]这个区间的区间在cow[i]之前。

样例实现:

此处只演示较为复杂的排序部分。

  1. 输入[1 2][0 3][3 4]
  2. 先排序
  3. 排序完后为[3,4][1,2][0,3]
  4. 那么此时能覆盖[1,2]的个数为1,[0,3]的个数为0(不符合S>S),[3,4]的个数为0
  5. 输出1,0,0

此演示省略了判断与查询、更新的部分,请谅解。

源码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int cas=1,T;
int cnt[100100];
int ans[100010];

struct node{
	int s,e,index;
}cow[100010];

int n;
int lowbit(int x){
	return x&(-x);
}
int sum(int x){
	int ans=0;
	while(x>0){
		ans+=cnt[x];
		x-=lowbit(x);
	}
	return ans;
}
void add(int x,int v){
	while (x<=100010){
		cnt[x]+=v;
		x+=lowbit(x);
	}
}
bool cmp(node a,node b){
    if(a.e!=b.e)
	    return a.e>b.e;
    return a.s<b.s;
}

int main(){
	while(scanf("%d",&n)!=EOF && n){
		memset(ans,0,sizeof(ans));
		memset(cnt,0,sizeof(cnt));
		for(int i=0;i<n;i++){
			scanf("%d%d",&cow[i].s,&cow[i].e);
			cow[i].s++;
			cow[i].e++;
			cow[i].index=i;
		}
		sort(cow,cow+n,cmp);
	    ans[cow[0].index]=sum(cow[0].s);
		add(cow[0].s,1);
		for(int i=1;i<n;i++){
			if (cow[i].s==cow[i-1].s && cow[i].e==cow[i-1].e)
				ans[cow[i].index]=ans[cow[i-1].index];
			else
				ans[cow[i].index]=sum(cow[i].s);
			add(cow[i].s,1);
		}
		for (int i=0;i<n-1;i++)
			printf("%d ",ans[i]);
		printf("%d\n",ans[n-1]);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/Wolfbeyond/p/9452962.html

时间: 2024-08-01 13:19:44

树状数组-例题的相关文章

BIT 树状数组 详解 及 例题

(一)树状数组的概念 如果给定一个数组,要你求里面所有数的和,一般都会想到累加.但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组中的元素后,仍然要你求数组中某段元素的和,就显得麻烦了.所以我们就要用到树状数组,他的时间复杂度为O(lgn),相比之下就快得多.下面就讲一下什么是树状数组: 一般讲到树状数组都会少不了下面这个图: 下面来分析一下上面那个图看能得出什么规律: 据图可知:c1=a1,c2=a1+a2,c3=a3,c4

线段树或树状数组求逆序数(附例题)

学习了博主:MyZee   , shengweison 的文章 线段树或树状数组求逆序数 假设给你一个序列 6 1 2 7 3 4 8 5,  首先我们先手算逆序数, 设逆序数为 N; 6的前面没有比他大的数 N +=0 1的前面有一个比他大的数 N+=1 2的前面有一个比他大的数 N+=1 7的前面没有比他大的数 N+=0 ... 最后得到 N = 0 + 1 + 1 + 0 + 2 + 2 + 0 + 3 = 9 其实我们可用用线段树,或者树状数组模拟这个过程. 又因为线段树和树状数组的效率

poj 2299 Ultra-QuickSort 离散化 + 树状数组

题目链接:http://poj.org/problem?id=2299 离散化 + 树状数组 教科书例题般的题目 #include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <cmath> #include <vector> #include <stack> #include <set> #include

树状数组模板(改点求段 / 该段求点 / 改段求段)

1. 改点求段(单点更新, 区间求和) 代码: 1 #include <iostream> 2 using namespace std; 3 4 const int MAXN = 1e5 + 10; 5 int tree[MAXN], n; 6 7 int lowbit(int x){//返回 pow(2, k),其中k为末尾0的个数, 即返回最低位1的值 8 return x & -x; 9 } 10 11 void add(int x, int d){//将d累加到tree数组对应

树状数组总结

树状数组 数据结构知识点1-树状数组 树状数组的用途就是维护一个数组,重点不是这个数组,而是要维护的东西,最常用的求区间和问题,单点更新.但是某些大牛YY出很多神奇的东西,完成部分线段树能完成的功能,比如区间更新,区间求最值问题. 树状数组当然是跟树有关了,但是这个树是怎么构建的呐?这里就不得不感叹大牛们的脑洞之大了,竟然能想出来用二进制末尾零的个数多少来构建树以下图为例: 从上图能看出来每一个数的父节点就是右边比自己末尾零个数多的最近的一个,也就是x的父节点就是x+(x&(-x)),这里为什么

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的). 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上).所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够

【学习整理】树状数组 区间修改+查询

前言:对于区间修改和区间查询这样的简单问题,打一大堆线段树确实是不划算,所以学习了区间修改+区间修查询的树状数组. 我们定义 为原数列, ,显然  . 若想要将区间 的数全部 则只需要将 , 即可. 所以,我们维护一个数组 在将区间 的数全部 则还需同时将 , . 结论: 例题:CODEVS1082 线段树练习3 http://codevs.cn/problem/1082/ #include<iostream> #include<cstdio> #include<cstdli

UVALive 4329 Ping pong (树状数组)

白书上的例题.花了很多时间在找bug上,刚学树状数组,这道题add操作时要注意上限不是n. #include <bits/stdc++.h> using namespace std; #define ll long long const ll maxn = 1e5 + 10; ll C[maxn]; ll n; inline ll lowbit(ll x) { return x & (-x); } ll sum(ll x) { ll ret = 0; while(x > 0) {

数据结构:树状数组

关于树状数组的概述,可以看一下这篇博客:http://blog.csdn.net/int64ago/article/details/7429868 树状数组是一个可以高效地进行区间统计的数据结构,在思想上类似于线段树,比线段树节省空间,编程复杂度比线段树低,但适用范围比线段树小.主要工作也是查询和更新. 例题:POJ - 2352    (http://poj.org/problem?id=2352) 题目大意:输入n个星星坐标,坐标按y递增顺序输入,y相同按x递增顺序输入.定义一个星星的级别是