题目
题意:给定由n个横街道和m个纵街道组成的地图(其中边界0和1000000默认都是街道);给定k个在街道上的行人(显然这些行人所在的横或纵坐标至少有一个在街道上)。求有多少对人,他们之间的最短距离不是曼哈顿距离(当然,两个人之间只能通过街道走路,不能穿墙)。
思路:在草稿纸上画一画,可以较容易的得出结论。对于所在横、纵坐标都处于街道上的人(如下图小黄),他去任何其他人的最短距离都是最短距离。

对于只有横或纵坐标处于街道上的人(如下图小黄),不失一般性,不妨设该人所在横坐标在街道上,设离他最近的左纵街道为L,右纵街道为R,那么小黄和L左边的人,以及右边的人的最短距离都是曼哈顿距离。小黄和(L,R)中的距离不是最短距离(如下图浅绿),但需要排除(L,R)中和小黄同个街道的人(如下图深绿)。因此,我们可以按顺序添加行人。计算浅绿人头,可以用区间查询,分别维护横和纵方向的线段树;计算深绿人头,可以用二分,维护每条街道的有序集合(代码用的是set)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 200010;
const int maxm = 300010;
const int mx = 1000001;
#define lson (rt << 1)
#define rson (rt << 1 | 1)unordered_set<int> X, Y;
unordered_map<int, set<int> > mp[2];
int arr[2][maxn];
int n, m, k;
int x[maxm], y[maxm];
int len[2], p[2];
int sum[2][(mx + 5) << 2];void pushup(int rt, int pos) {sum[pos][rt] = sum[pos][lson] + sum[pos][rson];
}ll query(int rt, int l, int r, int a, int b, int pos) {if (a <= l && r <= b) {return sum[pos][rt];}int m = (l + r) / 2;ll res = 0;if (a <= m) res += query(lson, l, m, a, b, pos);if (m < b) res += query(rson, m + 1, r, a, b, pos);return res;
}void update(int rt, int l, int r, int x, int val, int pos) {if (l == r) {sum[pos][rt] += val;return;}int m = (l + r) / 2;if (x <= m) update(lson, l, m, x, val, pos);else update(rson, m + 1, r, x, val, pos);pushup(rt, pos);
}
// 查找最大的小于val的下标
int binaryL(int pos, int val) { int l = 0, r = len[pos] - 1;while (l < r) {int m = (l + r + 1) / 2;if (arr[pos][m] < val) {l = m;} else {r = m - 1;}}return l;
}
int main() {int t;scanf("%d", &t);memset(sum, 0, sizeof(sum));// build 线段树可以复用 while (t--) {scanf("%d%d%d", &n, &m, &k);X.clear();mp[0].clear();// 下标都加一处理 for (int i = 0; i < n; ++i) {scanf("%d", &arr[0][i]);X.insert(++arr[0][i]);mp[0][arr[0][i]] = set<int>();}Y.clear();mp[1].clear();for (int i = 0; i < m; ++i) {scanf("%d", &arr[1][i]);Y.insert(++arr[1][i]);mp[1][arr[1][i]] = set<int>();}
//      build(1, 1, mx)ll ans = 0;len[0] = n;len[1] = m;mp[0].clear();mp[1].clear();for (int i = 0; i < k; ++i) {scanf("%d%d", &x[i], &y[i]);p[0] = ++x[i];p[1] = ++y[i];// 超级小黄,到哪都是曼哈顿 if (X.find(p[0]) != X.end() && Y.find(p[1]) != Y.end()) {//              printf("(%d, %d) +%d\n", p[0]-1, p[1]-1, 0);continue;}int pos = 0;if (Y.find(p[1]) != Y.end()) {pos = 1;}int pos2 = 1 - pos;int l = binaryL(pos2, p[pos2]);int r = l + 1;int L = arr[pos2][l] + 1, R = arr[pos2][r] - 1;
//          int r = binaryR(1 - pos, p[1-pos]);mp[pos][p[pos]].insert(p[pos2]);// 更新街道集合,先插入,再做查询 ll tmp = query(1, 1, mx, L, R, pos2);//计算浅绿人头+深绿人头 ll tmp2 = distance(mp[pos][p[pos]].lower_bound(L),// 计算深绿人头 mp[pos][p[pos]].upper_bound(R)) - 1;// 减去小黄自己
//          printf("(%d, %d) +%d-%d %s:[%d, %d] - %s:mp[%d]\n", p[0] - 1, p[1] - 1, tmp, tmp2,
//                  pos2 == 0 ? "line" : "col", L - 1, R - 1, pos2 == 0 ? "col" : "line", p[pos] - 1);ans += tmp - tmp2;// 更新线段树 update(1, 1, mx, p[1], 1, 1);update(1, 1, mx, p[0], 1, 0);}printf("%lld\n", ans);// 清空线段树 for (int i = 0; i < k; ++i) {p[0] = x[i];p[1] = y[i];if (X.find(p[0]) != X.end() && Y.find(p[1]) != Y.end()) {continue;}update(1, 1, mx, p[1], -1, 1);update(1, 1, mx, p[0], -1, 0);}}
}
/*
4
5 4 9
0 1 2 6 1000000
0 4 8 1000000
4 4
2 5
2 2
6 3
1000000 1
3 8
5 8
8 8
6 8*/

Inconvenient Pairs(线段树/二分)相关推荐

  1. 牛客小白月赛28 E-会当凌绝顶,一览众山小 线段树+二分暴力模拟

    牛客小白月赛28 E-会当凌绝顶,一览众山小 线段树+二分暴力模拟 题意 思路 Code 传送门: https://ac.nowcoder.com/acm/contest/16081/E 题意 登山顺 ...

  2. codeforces 609F Frogs and mosquitoes 线段树+二分+multiset

    http://codeforces.com/problemset/problem/609/F There are n frogs sitting on the coordinate axis Ox. ...

  3. HDU - 4614 Vases and Flowers 线段树+二分

    题目链接 思路:线段树维护区间和,当k=1时,询问二分询问[x-(x~n-1)]找到最小位置,复杂度n*logn*logn卡过 #include<stdio.h> #include< ...

  4. 可持久化普通线段树 ---- P2839 [国家集训队]middle 可持久化普通线段树 + 二分 求中位数最大值

    题目链接 题目大意: 解题思路: 这个题思路很妙!! 首先我们假设只有一次询问怎么做? 那么我们可以二分出这个最大值midmidmid,然后把大于等于midmidmid设置成111,把小于midmid ...

  5. P1083 借教室(标记永久化线段树/二分+前缀和)难度⭐⭐⭐★

    P1083 借教室 标记永久化线段树 很典型的区间修改问题,先输入赋值建树(这就是最典型的线段树呀,别忘了),然后修改 这里问的是是否有足够的空教室,所以线段树中 min 代表的是当前区间内最小的剩余 ...

  6. CodeForces - 1454F Array Partition(线段树+二分)

    题目链接:点击查看 题目大意:给出一个长度为 n 的序列,现在要求求出任意一组 x , y , z,满足下列条件: x + y + z = n max( 1 , x ) = min( x + 1 , ...

  7. CodeForces - 1354D Multiset(线段树/二分)

    题目链接:点击查看 题目大意:规定在一个 multiset 中初始时有 n 个元素,随后有 m 次操作,每次操作给出一个 num: num > 0 时:向 multiset 中添加 num nu ...

  8. [ZJOI2015] 幻想乡战略游戏(树链剖分 + 线段树二分 + 带权重心)

    problem luogu-P3345 solution 这是一个带权重心的题,考察动态点分治.点分治?呵,不可能的,这辈子都不可能写点分治 我们重新考虑重心的性质:以这个点为根时,所有子树的大小不会 ...

  9. P2824-[HEOI2016/TJOI2016]排序【线段树,二分】

    正题 题目链接:https://www.luogu.com.cn/problem/P2824 题目大意 nnn个数,每次将一个区间正序或者倒序排序,求最后位置ppp的数. 解题思路 思路确实巧妙 二分 ...

最新文章

  1. php怎么返回json格式的数据
  2. java spring-webflux netty
  3. Hadoop hdfs配置
  4. ‘entityManagerFactory‘ that could not be found
  5. API网关正在经历身份危机
  6. java部署jar还是war优劣_详解Spring Boot 部署jar和war的区别
  7. jvm--Garbage Collection
  8. 手把手教你学习网络编程(1)
  9. mysql库迁移_MySql数据库 指定库迁移
  10. 使用tail和head读取字节流
  11. springcloud断点续传源码_SpringCloud 超大文件上传和断点续传的实现
  12. 编程之美第一篇 01分数规划
  13. oracle避免索引失效,Oracle优化你的查询--关于避免索引隐式失效
  14. 使用Vue指令实现下拉菜单效果
  15. 我也说说《变形金刚》2
  16. 利用Python软件完成通讯录功能
  17. vim 编辑器常用操作
  18. Xmind 无法保存文件
  19. python中的列表是什么意思_python中列表的用法是什么
  20. SAP HANA client2.0 下载网站

热门文章

  1. 搭建祥云首发最新正版代刷网最新可用源码
  2. TI电机控制库 SVGEN_DQ and FC_PWM_DRV问题的一些理解
  3. STM32蓝牙通信控制LED(HC-08)
  4. Unity3D 物体碰撞、物理碰撞材质
  5. Es6-Es13使用方法及特性
  6. 一边是马云,一边是星云
  7. Maven问题总结及配置私服或公共仓库
  8. 免费视频资源 网站 网易学院 webcast csdn课堂 等等
  9. 新浪sina邮箱客户端配置
  10. 两大方向各有机会,CRM SaaS还能怎么玩?