51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)
- 第1行:1个数N,N为点的数量(0 <= N <= 50000)
- 第2 - N + 1行:N个点的坐标,坐标为整数。(0 <= X[i], Y[i] <= 10^9)
- 输出斜率小于0的连线的数量。(2,3) (2,4)以及(2,3) (3,3)这2种情况不统计在内。
- 4
- 2 3
- 3 4
- 1 5
- 4 6
- 2

问你斜率小于0的两点的有多少个
这题跟poj 2352很相像
那题是求某个点左下角点的个数,本题是求某个点左上角点的个数
那题输入是由规则的(按照y的升序,y相等的话,x小的在前)
所以这题一开始也要按照这个规则先排一下序
如果我们直接求某个点左上角的点的个数的话,直接改一下那题就可以了,但是有一点麻烦的地方:
那个题处于做下角边界的情况也是统计在内的
但是这题不行
所以有点麻烦
但是我们可以转化一下思路
因为一开始按照那个排序规则是已经排好序了的
所以当前点肯定是y最大的点,那么以该点为分界点
和该点斜率大于等于0或者斜列不存在的点肯定都是位于该点的左下角的
那么这个时候所有点的数量(不包括当前点)减去当前点左下角的点的数量(包括左下角的边界)
就是斜率小于0的点的数量
即就是该点右下角点的数量(不包括边界的,边界的前面左下角算过了)
所以
先将输入的数据按照x离散化一下,因为每次getsum都是传x进去的
具体操作:
数据先按照x升序,x相等则y小的放前面的规则排序
因为每次getsum都是传x进去,所以按照x优先的规则排序好
排序好之后给按照顺序给点一个编号
这个就是所谓的离散化
然后getsum传进去的就是编号
具体理解一下,编号间的大小顺序关系和x原来的大小顺序关系是一样的
只是数据的范围被压缩了,但是期间各个数据的关系是没有变的
所以是可以这样的
这个就是离散化
就是跟star那题差不多了,按照y升序,y相等则x小的放前面的规则排序
然后每次getsum的时候得到的是左下角点的数量
当前点总数(除当前点)减去当前点左下角点的数量
就是当前点右下角的数量
就是和当前点斜率小于0的点的数量
然后将每次getsum的答案加起来就是所有斜率小于0的点对的数量
- #include<queue>
- #include<set>
- #include<cstdio>
- #include <iostream>
- #include<algorithm>
- #include<cstring>
- #include<cmath>
- using namespace std;
- #define max_v 500005
- struct node
- {
- int x,y;
- int num;
- } p[max_v];
- bool cmp1(node a,node b)//树状数组操作需要
- {
- if(a.y!=b.y)
- return a.y<b.y;
- else
- return a.x<b.x;
- }
- bool cmp2(node a,node b)//离散化需要
- {
- if(a.x!=b.x)
- return a.x<b.x;
- else
- return a.y<b.y;
- }
- int c[max_v];
- int maxx;
- int lowbit(int x)
- {
- return x&(-x);
- }
- void update(int x,int d)
- {
- while(x<=maxx)
- {
- c[x]+=d;
- x+=lowbit(x);
- }
- }
- int getsum(int x,int num)
- {
- int res=;
- while(x>)
- {
- res+=c[x];
- x-=lowbit(x);
- }
- return num-res;//当前点总数减去该点左下角点的数量就是右下角点的数量
- }
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- memset(c,,sizeof(c));
- for(int i=; i<n; i++)
- {
- scanf("%d %d",&p[i].x,&p[i].y);
- }
- //离散化
- sort(p,p+n,cmp2);
- int k=;
- p[].num=k;
- for(int i=; i<n; i++)
- {
- p[i].num=++k;
- }
- maxx=k;
- //树状数组
- sort(p,p+n,cmp1);
- long long ans=;
- for(int i=; i<n; i++)
- {
- ans+=getsum(p[i].num,i);//注意是i,不是i+1,因为当前点总数不包括当前点自己
- update(p[i].num,);
- }
- printf("%I64d\n",ans);
- }
- return ;
- }
- /*
- 题目意思:
- 问你斜率小于0的两点的有多少个
- 分析:
- 这题跟poj 2352很相像
- 那题是求某个点左下角点的个数,本题是求某个点左上角点的个数
- 那题输入是由规则的(按照y的升序,y相等的话,x小的在前)
- 所以这题一开始也要按照这个规则先排一下序
- 如果我们直接求某个点左上角的点的个数的话,直接改一下那题就可以了,但是有一点麻烦的地方:
- 那个题处于做下角边界的情况也是统计在内的
- 但是这题不行
- 所以有点麻烦
- 但是我们可以转化一下思路
- 因为一开始按照那个排序规则是已经排好序了的
- 所以当前点肯定是y最大的点,那么以该点为分界点
- 和该点斜率大于等于0或者斜列不存在的点肯定都是位于该点的左下角的
- 那么这个时候所有点的数量(不包括当前点)减去当前点左下角的点的数量(包括左下角的边界)
- 就是斜率小于0的点的数量
- 即就是该点右下角点的数量(不包括边界的,边界的前面左下角算过了)
- 还有一个问题就是数据太大了,c数组开不了,需要离散化
- 所以
- 先将输入的数据按照x离散化一下,因为每次getsum都是传x进去的
- 具体操作:
- 数据先按照x升序,x相等则y小的放前面的规则排序
- 因为每次getsum都是传x进去,所以按照x优先的规则排序好
- 排序好之后给按照顺序给点一个编号
- 这个就是所谓的离散化
- 然后getsum传进去的就是编号
- 具体理解一下,编号间的大小顺序关系和x原来的大小顺序关系是一样的
- 只是数据的范围被压缩了,但是期间各个数据的关系是没有变的
- 所以是可以这样的
- 这个就是离散化
- 离散化完成之后
- 就是跟star那题差不多了,按照y升序,y相等则x小的放前面的规则排序
- 然后每次getsum的时候得到的是左下角点的数量
- 当前点总数(除当前点)减去当前点左下角点的数量
- 就是当前点右下角的数量
- 就是和当前点斜率小于0的点的数量
- 然后将每次getsum的答案加起来就是所有斜率小于0的点对的数量
- */
思路二:
转换为求逆序数,然后归并求逆序
(xi-xj)/(yi-yj)<0 的点对的个数
现在我们先按照x升序,x相同的则y小的放前面规则排好序
假设i<j
那么xi-xj肯定是小于0的
那么斜率要求小于0,则要求yi-yj大于0
则就是求排好序之后所有的y组成的序列的逆序数
它的逆序数就是斜率小于0点对的个数!
所以先按照那个规则排好序
然后将y拿出来组成一个数组
用归并排序求数列的逆序数
归并可以求逆序的理由:
将两个已经排好序的数组合并,
此时,若a[i] > a[j],则i到m之间的数都大于a[j],
合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数,
而小于等于的情况并不会产生。
- #include<stdio.h>
- #include<memory>
- #include<algorithm>
- using namespace std;
- #define max_v 500005
- typedef long long LL;
- struct node
- {
- int x,y;
- }p[max_v];
- int a[max_v];
- bool cmp(node a,node b)
- {
- if(a.x!=b.x)
- return a.x<b.x;
- else
- return a.y<b.y;
- }
- int temp[max_v];
- LL ans;
- void mer(int s,int m,int t)
- {
- int i=s;
- int j=m+;
- int k=s;
- while(i<=m&&j<=t)
- {
- if(a[i]<=a[j])
- {
- temp[k++]=a[i++];
- }else
- {
- ans+=j-k;//求逆序数
- temp[k++]=a[j++];
- }
- }
- while(i<=m)
- {
- temp[k++]=a[i++];
- }
- while(j<=t)
- {
- temp[k++]=a[j++];
- }
- }
- void cop(int s,int t)
- {
- for(int i=s;i<=t;i++)
- a[i]=temp[i];
- }
- void megsort(int s,int t)
- {
- if(s<t)
- {
- int m=(s+t)/;
- megsort(s,m);
- megsort(m+,t);
- mer(s,m,t);
- cop(s,t);
- }
- }
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- if(n==)
- break;
- ans=;
- for(int i=;i<n;i++)
- scanf("%d %d",&p[i].x,&p[i].y);
- sort(p,p+n,cmp);
- for(int i=;i<n;i++)
- {
- a[i]=p[i].y;
- }
- megsort(,n-);
- printf("%lld\n",ans);
- }
- return ;
- }
- /*
- 从斜率的公式入手
- (xi-xj)/(yi-yj)<0 的点对的个数
- 现在我们先按照x升序,x相同的则y小的放前面规则排好序
- 假设i<j
- 那么xi-xj肯定是小于0的
- 那么斜率要求小于0,则要求yi-yj大于0
- 则就是求排好序之后所有的y组成的序列的逆序数
- 它的逆序数就是斜率小于0点对的个数!
- 所以先按照那个规则排好序
- 然后将y拿出来组成一个数组
- 用归并排序求数列的逆序数
- 归并可以求逆序的理由:
- 在归并排序的过程中,比较关键的是通过递归,
- 将两个已经排好序的数组合并,
- 此时,若a[i] > a[j],则i到m之间的数都大于a[j],
- 合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数,
- 而小于等于的情况并不会产生。
- */
51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)的更多相关文章
- 51Nod - 1107 斜率小于0的连线数量
二维平面上N个点之间共有C(n,2)条连线.求这C(n,2)条线中斜率小于0的线的数量. 二维平面上的一个点,根据对应的X Y坐标可以表示为(X,Y).例如:(2,3) (3,4) (1,5) (4, ...
- 51NOD——N 1107 斜率小于0的连线数量
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1107 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 ...
- 剑指 Offer 51. 数组中的逆序对 + 归并排序 + 树状数组
剑指 Offer 51. 数组中的逆序对 Offer_51 题目描述 方法一:暴力法(双层循环,超时) package com.walegarrett.offer; /** * @Author Wal ...
- [luogu4479][BJWC2018]第k大斜率【二维偏序+二分+离散化+树状数组】
传送门 https://www.luogu.org/problemnew/show/P4479 题目描述 在平面直角坐标系上,有 n 个不同的点.任意两个不同的点确定了一条直线.请求出所有斜率存在的直 ...
- NOI.AC#2139-选择【斜率优化dp,树状数组】
正题 题目链接:http://noi.ac/problem/2139 题目大意 给出\(n\)个数字的序列\(a_i\).然后选出一个不降子序列最大化子序列的\(a_i\)和减去没有任何一个数被选中的 ...
- CF E. Vasya and a Tree】 dfs+树状数组(给你一棵n个节点的树,每个点有一个权值,初始全为0,m次操作,每次三个数(v, d, x)表示只考虑以v为根的子树,将所有与v点距离小于等于d的点权值全部加上x,求所有操作完毕后,所有节点的值)
题意: 给你一棵n个节点的树,每个点有一个权值,初始全为0,m次操作,每次三个数(v, d, x)表示只考虑以v为根的子树,将所有与v点距离小于等于d的点权值全部加上x,求所有操作完毕后,所有节点的值 ...
- 51nod 1107 斜率小于零连线数量 特调逆序数
逆序数的神题.... 居然是逆序数 居然用逆序数过的 提示...按照X从小到大排列,之后统计Y的逆序数... 之后,得到的答案就是传说中的解(斜率小于零) #include<bits/stdc+ ...
- UVa 11384 - Help is needed for Dexter 分析, 树状数组 难度: 0
题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...
- 51 Nod 1100 斜率最大
1100 斜率最大 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注 平面上有N个点,任意2个点确定一条直线,求出所有这些直线中,斜率最大的那条直线 ...
随机推荐
- JS判断客户端是否是iOS或者Android端
通过判断浏览器的userAgent,用正则来判断手机是否是 IOS 和 Android 客户端. 代码如下: (function(){ var u = navigator.userAgent; var ...
- flutter Row里面元素居中显示
直接上代码: new Expanded( flex: , child: new Row( children: <Widget>[ Expanded( child: new Containe ...
- android--Git上克隆项目遇到的坑
直接上图,首先你得有你得GitHub项目地址,如下: 然后打开android studio,选择新建项目时从Git上克隆: 点击clone等待完成,新窗口打开. 打开之后可能.或许.大概.也许会出现下 ...
- Android横、竖屏幕动态切换(layout-land 和layout-port)
下面是一个例子程序: 1.首先通过以下语句设置Activity为无标题和全屏模式: // 设置为无标题栏 requestWindowFeature(Window.FEATURE_NO_TITLE); ...
- maven 超级pom位置、maven命令、构件、下载位置、手动打包位置、中央仓库ip
1.超级pom位置 ----> 解压M2_HOME/lib/maven-model-builder-3.5.4.jar 2.运行maven 命令实际上是运行了 java 命令,因为maven插件 ...
- win7安装node.js
最新版本的node.js都已经集成了npm,所以直接从官网下载即可!(不用再配置环境变量) 官网:https://nodejs.org/download/release/latest/ 下载解压即安装 ...
- Oracle EBS 清除并发请求和(或)管理器数据 请求
请求说明:该请求可以清除平时提交的请求日志文件.并发管理器的日志文件.报表输出文件.并发请求和并发管理器进程的历史记录信息. 参数说明:(红色标注字段为必输项)1. 实体:ALL:清除请求历史记录. ...
- SQL SERVER Management Studio编写SQL时没有智能提示的解决方式
1. 检查设置里是否启用智能感知(Intellisence),可以在“工具”→“选项”里设置 2. 如果启用后还是无效,可以新建一个查询窗口查询,输入关键词的前面几个字母看是否有提示(或者使用Ctrl ...
- 细说C#继承
简介 继承(封装.多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性. 继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿.这个语义在生活中,就像 家族 ...
- sql面试
1.用一条SQL语句 查询出每门课都大于80分的学生姓名 name kecheng fenshu 张三 语文 81张三 数学 75李四 语文 ...