https://codeforces.com/contest/1191/problem/F

看了一下题解的思路,感觉除了最后一段以外没什么启发。

首先离散化x加快速度,免得搞多一个log。其实y不需要离散化。

规定无穷大就是xn+1这个很好理解嘿嘿。(反正开多了5个不怕)

注意到其实从上往下一行一行扫过去,每次必须新增的元素才是新的集合,那很容易想到一个不重不漏的办法就是每次计算“以点p[i]为加进去的新点中的结束的集合”,那么假设一开始p[i]的左侧有cntl个点,那么显然有(cntl+1)条线在p[i]的左侧,而p[i]的右侧有cntr个点,也是(cntr+1)条线。

这个cntl显然就是query(1,p[i].x-1),而右侧则是query(p[i].x+1,p[i+1].x-1),因为不能包含同y的下一个点p[i+1],而其中,上面的点选法也会产生区别。

那么每层加入一个正无穷也就是xn+1就可以了。

溢出这种现在我不会错的了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int n;
struct Point {
int x, y;
bool operator<(const Point &p)const {
return y == p.y ? x<p.x: y>p.y;
//y从上到下,x从左到右
}
} p[200005]; int x[200005];
int y[200005]; ll sum; const int MAXM = 200000;
int st[(MAXM << 2) + 5]; inline void push_up(int o) {
st[o] = st[o << 1] + st[o << 1 | 1];
} void build(int o, int l, int r) {
if(l == r) {
st[o] = 0;
} else {
int m = (l + r) >> 1;
build(o << 1, l, m);
build(o << 1 | 1, m + 1, r);
push_up(o);
}
} void update(int o, int l, int r, int x, int v) {
if(l == r) {
//不是加,是赋值,同x的点是没有差别的
st[o] = v;
return;
} else {
int m = (l + r) >> 1;
if(x <= m)
update(o << 1, l, m, x, v);
else if(x >= m + 1)
update(o << 1 | 1, m + 1, r, x, v);
push_up(o);
}
} int query(int o, int l, int r, int a, int b) {
if(b < a)
return 0;
else if(a <= l && r <= b) {
return st[o];
} else {
int m = (l + r) >> 1;
int ans = 0;
if(a <= m)
ans = query(o << 1, l, m, a, b);
if(b >= m + 1)
ans += query(o << 1 | 1, m + 1, r, a, b);
return ans;
}
} int vx[200005], vxtop; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
//freopen("Yinku.out", "w", stdout);
#endif // Yinku
while(~scanf("%d", &n)) {
for(int i = 1; i <= n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
x[i] = p[i].x;
y[i] = p[i].y;
}
sort(x + 1, x + 1 + n);
int xn = unique(x + 1, x + 1 + n) - (x + 1);
sort(y + 1, y + 1 + n);
int yn = unique(y + 1, y + 1 + n) - (y + 1);
for(int i = 1; i <= n; i++) {
p[i].x = lower_bound(x + 1, x + 1 + xn, p[i].x) - x;
p[i].y = lower_bound(y + 1, y + 1 + yn, p[i].y) - y;
//从1开始分配新的坐标
//printf("(%d,%d)\n", p[i].x, p[i].y);
}
sort(p + 1, p + 1 + n);
//扫描线
sum = 0;
build(1, 1, xn + 1);
int beg = 1, cur = 1;
while(beg <= n) {
vxtop = 0;
while(p[cur].y == p[beg].y) {
update(1, 1, xn + 1, p[cur].x, 1);
vx[++vxtop] = p[cur].x;
/*
//点是不会重合的,那包含这个最左侧的点的都是全新集合
int cntl = query(1, 1, xn, 1, p[cur].x - 1);
//在这个点的左侧有cntl个x不同的点,那就有cntl+1个位置
//sum += (cntl + 1); X
//是以这个点为右侧边界的,所以右侧没得选 X
*/
//该层y中是以这个x点为右侧边界,但是两个x点之间的上层y也是可选的
cur++;
}
vx[++vxtop] = xn + 1;
for(int i = 1; i <= vxtop - 1; i++) {
//该层最右端的新点为vx[i]的数量
int cntl = query(1, 1, xn + 1, 1, vx[i] - 1);
int cntr = query(1, 1, xn + 1, vx[i] + 1, vx[i + 1] - 1);
sum += 1ll * (cntl + 1) * (cntr + 1);
}
beg = cur;
}
printf("%lld\n", sum);
}
}

偶尔用下树状数组,把一些多余操作去掉之后的最快的做法。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int n;
struct Point {
int x, y;
bool operator<(const Point &p)const {
return y == p.y ? x<p.x: y>p.y;
//y从上到下,x从左到右
}
} p[200005]; int x[200005]; ll _sum; const int MAXM = 200005;
bool cntx[MAXM + 5];
int bit[MAXM + 5]; int upper; inline int sum(int x) {
int res = 0;
while(x) {
res = res + bit[x];
x -= x & -x;
}
return res;
} inline void update(int x) {
if(cntx[x])
return;
else {
cntx[x] = true;
while(x <= upper) {
bit[x] += 1;
x += x & -x;
}
}
} inline int range_sum(int x, int y) {
return sum(y) - sum(x - 1);
} int *vx=x,vxtop; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
//freopen("Yinku.out", "w", stdout);
#endif // Yinku
while(~scanf("%d", &n)) {
memset(cntx, false, sizeof(cntx));
for(int i = 1; i <= n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
x[i] = p[i].x;
}
sort(x + 1, x + 1 + n);
int xn = unique(x + 1, x + 1 + n) - (x + 1);
for(int i = 1; i <= n; i++) {
p[i].x = lower_bound(x + 1, x + 1 + xn, p[i].x) - x;
//从1开始分配新的坐标
//printf("(%d,%d)\n", p[i].x, p[i].y);
}
sort(p + 1, p + 1 + n);
//扫描线
_sum = 0;
upper = xn + 1;
int beg = 1, cur = 1;
while(beg <= n) {
vxtop = 0;
while(p[cur].y == p[beg].y) {
update(p[cur].x);
vx[++vxtop] = p[cur].x;
//该层y中是以这个x点为右侧边界,但是两个x点之间的上层y也是可选的
cur++;
}
vx[++vxtop] = xn + 1;
for(int i = 1; i <= vxtop - 1; i++) {
//该层最右端的新点为vx[i]的数量
int cntl = range_sum(1, vx[i] - 1);
int cntr = range_sum(vx[i] + 1, vx[i + 1] - 1);
_sum += 1ll * (cntl + 1) * (cntr + 1);
}
beg = cur;
}
printf("%lld\n", _sum);
}
}

最新文章

  1. js转换数据类型为浮点型,并取两位小数点
  2. Android Studio--Gradle基础(转)
  3. linux:手动校准系统时间和硬件CMOS时间
  4. dict.items vs six.iteritems
  5. Thread.Sleep in WinRT
  6. CentOS6.5升级手动安装gcc4.8.2
  7. c语言,strcmp(),字符串比较,看Asic 码,str1&gt;str2,返回值 &gt; 0;两串相等,返回
  8. php字符串处理函数常见问题
  9. php性能效率优化
  10. javascript调试
  11. ABP入门系列(2)——领域层创建实体
  12. 数据交换格式与SpringIOC底层实现
  13. js常用通用方法
  14. Windows 10文件夹Shirt+鼠标右键出现“在此处打开命令窗口”
  15. java.lang(StringBuffer)
  16. 淘宝可伸缩高性能互联网架构HSF(转)
  17. 机器学习之Apriori算法和FP-growth算法
  18. ubuntu apt 代理设置
  19. 树形dp(poj 1947 Rebuilding Roads )
  20. 算法笔记_163:算法提高 最大乘积(Java)

热门文章

  1. python面向对象的三大特征--封装
  2. 基于celery的任务管理
  3. css图像拼合技术(精灵图)
  4. JS中判断一个数组是否有相同数据的
  5. 探讨vue的双向绑定原理及实现
  6. Django2 + ORM 做一个简单的登陆
  7. 3,LinkedList
  8. spring boot 简介(基于SSM框架的一个升级版本吧)
  9. 5G即将到来!我们需要一部怎样的手机呢?
  10. 20180803-Java 流(Stream)、文件(File)和IO