Codeforces - 1191F - Tokitsukaze and Strange Rectangle - 组合数学 - 扫描线
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);
}
}
Codeforces - 1191F - Tokitsukaze and Strange Rectangle - 组合数学 - 扫描线的更多相关文章
- Codeforces 1190D. Tokitsukaze and Strange Rectangle
传送门 注意到矩形往上是无限的,考虑把点按 $y$ 从大到小考虑 对于枚举到高度为 $h$ 的点,设当前高度大于等于 $h$ 的点的所有点的不同的 $x$ 坐标数量为 $cnt$ 那么对于这一层高度 ...
- Tokitsukaze and Strange Rectangle CodeForces - 1191F (树状数组,计数)
大意: 给定$n$个平面点, 定义集合$S(l,r,a)$表示横坐标$[l,r]$纵坐标$[a,\infty]$内的所有点. 求可以得到多少种不同的集合. 从上往下枚举底层最右侧点, 树状数组统计贡献 ...
- CF1190D Tokitsukaze and Strange Rectangle
思路: 线段树 + 扫描线. 实现: #include <bits/stdc++.h> using namespace std; typedef long long ll; ; int n ...
- Codeforces 718A Efim and Strange Grade 程序分析
Codeforces 718A Efim and Strange Grade 程序分析 jerry的程序 using namespace std; typedef long long ll; stri ...
- [Codeforces 1191D] Tokitsukaze, CSL and Stone Game(博弈论)
[Codeforces 1191D] Tokitsukaze, CSL and Stone Game(博弈论) 题面 有n堆石子,两个人轮流取石子,一次只能从某堆里取一颗.如果某个人取的时候已经没有石 ...
- Codeforces - 1081C - Colorful Bricks - 简单dp - 组合数学
https://codeforces.com/problemset/problem/1081/C 这道题是不会的,我只会考虑 $k=0$ 和 $k=1$ 的情况. $k=0$ 就是全部同色, $k=1 ...
- Codeforces 1264D - Beautiful Bracket Sequence(组合数学)
Codeforces 题面传送门 & 洛谷题面传送门 首先对于这样的题目,我们应先考虑如何计算一个括号序列 \(s\) 的权值.一件非常显然的事情是,在深度最深的.是原括号序列的子序列的括号序 ...
- codeforces 677C C. Vanya and Label(组合数学+快速幂)
题目链接: C. Vanya and Label time limit per test 1 second memory limit per test 256 megabytes input stan ...
- 2018.10.25 bzo1227: [SDOI2009]虔诚的墓主人(组合数学+扫描线+bit)
传送门 有点难调啊.其实是我自己sb了 不过交上去1A1A1A还是平衡了一下心态. 所以这道题怎么做呢? 我们考虑对于一个点(x,y)(x,y)(x,y)如果这个点成为中心,正左/右/上/下分别有l/ ...
随机推荐
- linux--基础知识3
#linux虚拟终端切换 ctrl+alt+F(1-6) 总共支持六个终端切换 #mkdir /root/boot/目录 在指定路径下新建目录 #touch /目录/文件名 在指定目录创建文件 ...
- HTTP RESTful服务开发 spring boot+Maven +Swagger
这周配合第三方平台整合系统,需要提供HTTP REST服务和使用ActiveMQ推送消息,研究了下,做个笔记. 1.使用eclipse创建Spring Boot项目 创建Spring Boot项目( ...
- git上传文件夹的问题
使用git上传文件夹一定要注意,文件夹里面至少有一个文件,因为git不能管理空文件夹 所以上传就会不成功
- PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患(转)
PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患 时间 2014-11-14 15:05:49 WooYun知识库 原文 http://drops.wooyun.org/t ...
- MySQL重复数据中限定操作n条
对于一个表,有时可能里面有很多重复的条,比如: +-----------+---------+| coupon_id | user_id |+-----------+---------+| 8 | 1 ...
- Java中的Overload和Override有什么区别
Overload和Override的区别 1.Overload 定义 Overload是重载的意思.它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后在调用时,虚拟机就会 ...
- Test 6.23 T1 扫雷
题目背景 题目描述 输入格式 输出格式 样例输入输出 数据范围 解析 我们设两个作弊器的参数分别为\((a_1,b_1)\)和\((a_2,b_2)\),那么设 \[ S1=\frac{a_1}{b_ ...
- LeetCode--053--最大子序和(java)
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 ...
- LeetCode--041--缺失的第一个整数(java)
给定一个未排序的整数数组,找出其中没有出现的最小的正整数. 示例 1: 输入: [1,2,0] 输出: 3 示例 2: 输入: [3,4,-1,1] 输出: 2 示例 3: 输入: [7,8,9,11 ...
- [CSP-S模拟测试]:Merchant(二分答案)
题目描述 有$n$个物品,第$i$个物品有两个属性$k_i,b_i$,表示它在时刻$x$的价值为$k_i\times x+b_i$.当前处于时刻$0$,你可以选择不超过$m$个物品,使得存在某个整数时 ...