[离散化+树状数组]CodeForces - 652D Nested Segments
2 seconds
256 megabytes
standard input
standard output
You are given n segments on a line. There are no ends of some segments that coincide. For each segment find the number of segments it contains.
The first line contains a single integer n (1 ≤ n ≤ 2·105) — the number of segments on a line.
Each of the next n lines contains two integers li and ri ( - 109 ≤ li < ri ≤ 109) — the coordinates of the left and the right ends of the i-th segment. It is guaranteed that there are no ends of some segments that coincide.
Print n lines. The j-th of them should contain the only integer aj — the number of segments contained in the j-th segment.
4
1 8
2 3
4 7
5 6
3
0
1
0
3
3 4
1 5
2 6
0
1
1
题意:一条线上有n条直线,没有两个线段有相同的端点,要求每个线段各自包含了多少线段
下面有2种做法
思路1:
记录下右端点的坐标,记录下右端点的坐标,将右端点离散化,用树状数组维护右端点数量前缀和,
给左端点升序排序,从最左边的左端点开始找,
因为左端点升序,所以当前左端点要比所有右端点都小(一开始是满足的,接着我们要不断删去右端点来维护这种状态),当前左端点也比它后面的要小,如果它后面的左端点对应的右端点小于当前右端点,则那条线段是包含在当前线段中的),看当前这个右端点之前有多少个右端点就可知道当前这个线段包含了多少线段
因为我们要保证当前左端点要比所有右端点小才能使得只需统计当前右端点之前有多少右端点就能知道当前这个线段包含了多少线段,如果到了下一个左端点,则上一个线段就不包含在下一个线段中了,因此如果到了下一个左端点就要删掉上一个右端点,所以要删掉当前这个已经找过的右端点
思路2:
记录下右端点的坐标,记录下右端点的坐标,将右端点离散化,用树状数组维护右端点数量前缀和,
给左端点降序排序,从最右边的左端点开始找
因为左端点降序排序,所以当前左端点后都没有比它大的未计算过的左端点,如果比它大的左端点被计算过了且那个左端点对应的右端点比现在这个左端点对应的右端点小,则那条线段必定被当前线段包含,所以按左端点降序的顺序添加对应的右端点,可以保证当前没有右端点比当前左端的小
总结:
两种思路的共同点都是要保证当前左端点之前没有比它小的右端点,这样就只需要统计当前左端点对应的右端点之前有多少个右端点就可以知道当前这条线段包含了多少线段,他们的不同点在于思路1是提前把所有的右端的都加进去了,每次查询的时候再删,而思路2是先从最大的左端点开始添加右端点
思路1代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int amn=2e5+;
int bit[amn],n,tp,b[amn];
struct node{
int l,r,id;
} a[amn];
int ans[amn],jg[amn],pos;
map<ll,int> mp;
bool cmp(node a,node b){
if(a.r==b.r)return a.r<b.r;
return a.l<b.l;
}
int sum(int i){
int s=;
while(i){
s+=bit[i];
i-=i&-i;
}
return s;
}
void add(int i,int x){
while(i<=tp){
bit[i]+=x;
i+=i&-i;
}
}
int main(){
ios::sync_with_stdio();
cin>>n;
tp=;
for(int i=;i<=n;i++){
a[i].id=i; ///记录原始顺序
cin>>a[i].l>>a[i].r;
b[++tp]=a[i].r; ///记录下右端点的坐标
ans[i]=; ///初始化答案数组
}
sort(b+,b++tp); ///给右端点的坐标排序
int mtp=;
for(int i=;i<=tp;i++){
mp[b[i]]=mtp; ///将右端点离散化
mtp++;
}
for(int i=;i<=n;i++){
add(mp[a[i].r],); ///用树状数组维护右端点数量前缀和
}
sort(a+,a++n,cmp); ///给左端点升序排序,从最左边的左端点开始找
for(int i=;i<=n;i++){
ans[a[i].id]=sum(mp[a[i].r]-); ///因为左端点升序,所以当前左端点要比所有未计算过的右端点都小(一开始是满足的,接着我们要不断删去右端点来维护这种状态),当前左端点也比它后面的要小,如果它后面的左端点对应的右端点小于当前右端点,则那条线段是包含在当前线段中的),看当前这个右端点之前有多少个右端点就可知道当前这个线段包含了多少线段
add(mp[a[i].r],-); ///因为我们要保证当前左端点要比所有右端点小才能使得只需统计当前右端点之前有多少右端点就能知道当前这个线段包含了多少线段,如果到了下一个左端点,则上一个线段就不包含在下一个线段中了,因此如果到了下一个左端点就要删掉上一个右端点,所以要删掉当前这个已经找过的右端点
}
for(int i=;i<=n;i++){
printf("%d\n",ans[i]);
}
}
/***
一条线上有n条直线,没有两个线段有相同的端点,要求每个线段各自包含了多少线段
记录下右端点的坐标,记录下右端点的坐标,将右端点离散化,用树状数组维护右端点数量前缀和,
给左端点升序排序,从最左边的左端点开始找,
因为左端点升序,所以当前左端点要比所有未计算过的右端点都小(一开始是满足的,接着我们要不断删去右端点来维护这种状态),当前左端点也比它后面的要小,如果它后面的左端点对应的右端点小于当前右端点,则那条线段是包含在当前线段中的),看当前这个右端点之前有多少个右端点就可知道当前这个线段包含了多少线段
因为我们要保证当前左端点要比所有右端点小才能使得只需统计当前右端点之前有多少右端点就能知道当前这个线段包含了多少线段,如果到了下一个左端点,则上一个线段就不包含在下一个线段中了,因此如果到了下一个左端点就要删掉上一个右端点,所以要删掉当前这个已经找过的右端点
***/
思路2代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int amn=2e5+;
int bit[amn],n,tp,b[amn];
struct node{
int l,r,id;
} a[amn];
int ans[amn],jg[amn],pos;
map<ll,int> mp;
bool cmp1(node a,node b){
if(a.r==b.r)return a.r>b.r;
return a.l>b.l;
}
int sum(int i){
int s=;
while(i){
s+=bit[i];
i-=i&-i;
}
return s;
}
void add(int i,int x){
while(i<=tp){
bit[i]+=x;
i+=i&-i;
}
}
int main(){
ios::sync_with_stdio();
cin>>n;
tp=;
for(int i=;i<=n;i++){
a[i].id=i; ///记录原始顺序
cin>>a[i].l>>a[i].r;
b[++tp]=a[i].r; ///记录下右端点的坐标
ans[i]=; ///初始化答案数组
}
sort(b+,b++tp); ///给右端点的坐标排序
int mtp=;
for(int i=;i<=tp;i++){
mp[b[i]]=mtp; ///将右端点离散化
mtp++;
}
sort(a+,a++n,cmp1); ///给左端点降序排序,从最右边的左端点开始找
for(int i=;i<=n;i++){
add(mp[a[i].r],); ///因为左端点降序排序,所以当前左端点后都没有比它大的未计算过的左端点,如果比它大的左端点被计算过了且那个左端点对应的右端点比现在这个左端点对应的右端点小,则那条线段必定被当前线段包含,所以按左端点降序的顺序添加对应的右端点,可以保证当前没有右端点比当前左端的小
ans[a[i].id]=sum(mp[a[i].r]-);
}
for(int i=;i<=n;i++){ ///两种思路的共同点都是要保证当前左端点之前没有比它小的右端点,这样就只需要统计当前左端点对应的右端点之前有多少个右端点就可以知道当前这条线段包含了多少线段,他们的不同点在于思路1是提前把所有的右端的都加进去了,每次查询的时候再删,而思路2是先从最大的左端点开始添加右端点
printf("%d\n",ans[i]);
}
}
/***
一条线上有n条直线,没有两个线段有相同的端点,要求每个线段各自包含了多少线段
记录下右端点的坐标,记录下右端点的坐标,将右端点离散化,用树状数组维护右端点数量前缀和,
给左端点降序排序,从最右边的左端点开始找
因为左端点降序排序,所以当前左端点后都没有比它大的未计算过的左端点,如果比它大的左端点被计算过了且那个左端点对应的右端点比现在这个左端点对应的右端点小,则那条线段必定被当前线段包含,所以按左端点降序的顺序添加对应的右端点,可以保证当前没有右端点比当前左端的小
两种思路的共同点都是要保证当前左端点之前没有比它小的右端点,这样就只需要统计当前左端点对应的右端点之前有多少个右端点就可以知道当前这条线段包含了多少线段,他们的不同点在于思路1是提前把所有的右端的都加进去了,每次查询的时候再删,而思路2是先从最大的左端点开始添加右端点
***/
[离散化+树状数组]CodeForces - 652D Nested Segments的更多相关文章
- Code Forces 652D Nested Segments(离散化+树状数组)
		
Nested Segments time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...
 - CodeForces 540E - Infinite Inversions(离散化+树状数组)
		
花了近5个小时,改的乱七八糟,终于A了. 一个无限数列,1,2,3,4,...,n....,给n个数对<i,j>把数列的i,j两个元素做交换.求交换后数列的逆序对数. 很容易想到离散化+树 ...
 - HDU 5862 Counting Intersections(离散化+树状数组)
		
HDU 5862 Counting Intersections(离散化+树状数组) 题目链接http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 D ...
 - Ultra-QuickSort(归并排序+离散化树状数组)
		
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 50517 Accepted: 18534 ...
 - CodeForces 652D Nested Segments
		
离散化+树状数组 先对坐标离散化,把每条线段结尾所在点标1, 询问某条线段内有几条线段的时候,只需询问这段区间的和是多少,询问结束之后再把这条线段尾部所在点标为0 #include<cstdio ...
 - BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组
		
BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组 Description 酷爱日料的小Z经常光顾学校东门外的回转寿司店.在这里,一盘盘寿司通过传送带依次呈现在小Z眼前.不同的寿 ...
 - poj-----Ultra-QuickSort(离散化+树状数组)
		
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 38258 Accepted: 13784 ...
 - hdu 3015 Disharmony Trees (离散化+树状数组)
		
Disharmony Trees Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
 - 【bzoj4627】[BeiJing2016]回转寿司  离散化+树状数组
		
题目描述 给出一个长度为n的序列,求所有元素的和在[L,R]范围内的连续子序列的个数. 输入 第一行包含三个整数N,L和R,分别表示寿司盘数,满意度的下限和上限. 第二行包含N个整数Ai,表示小Z对寿 ...
 
随机推荐
- 彪悍的Surface Book2发布:能重拾笔记本行业的信心吗?
			
 Book2发布:能重拾笔记本行业的信心吗?" title="彪悍的Surface Book2发布:能重拾笔记本行业的信心吗?">  在智能手机全面普及之后, ...
 - 从租人APP沦为性工作发布平台 看共享经济监管边界
			
看共享经济监管边界" title="从租人APP沦为性工作发布平台 看共享经济监管边界"> 继直播类软件部分涉黄之后,最近火爆的各类"租人"软件 ...
 - 银行储蓄程序(C++,simple)
			
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
 - Oracle密码验证函数与Create Profile
			
今天看到了一个oracle密码函数的东西,就在网上找文档自己做测试,刚开始看不懂,最后做完记录一下 密码函数的作用就是要将用户密码进行限制,比如申请一个网站的账号的时候,密码会要求你不少于8位,必须要 ...
 - 常见WAF绕过思路
			
WAF分类 0x01 云waf 在配置云waf时(通常是CDN包含的waf),DNS需要解析到CDN的ip上去,在请求uri时,数据包就会先经过云waf进行检测,如果通过再将数据包流给主机. 0x02 ...
 - cooking和session
			
## Cookie ## # 基本概念: >> 用于保存一次会话中的记录,存放在客户端(浏览器); |-- "一次会话" |-- 当客户端打开一个界面时 被称作一次会话 ...
 - 前端面试题-<!DOCTYPE>
			
现在的各种前端开发工具都足够强大,支持插入模板代码,也就导致我们往往会忽略已经自动生成的代码,而代码的第一行 DOCTYPE 声明,就是最容易忽略的部分. 一.DOCTYPE DOCTYPE 是 do ...
 - Flutter 拖拽排序组件 ReorderableListView
			
注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 ReorderableListView是通过长按拖动某一项 ...
 - 趣谈编程史第3期-大器晚成的新晋流量Python发展史
			
写在前面 这篇博文主要介绍javaScript的发展史,根据作者在B站发布的同名视频的文案整理修改而成,对视频感兴趣的博友可访问https://www.bilibili.com/video/av860 ...
 - 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置
			
技术开发故事会连载 这是音视频基础学习系列的第一篇文章,主要讲解 SDL 是什么以及为什么要用到它,看似和音视频没啥卵关系,其实必不可少. SDL 简介 SDL 是 "Simple Dire ...