Description

  

  链接

  

Solution

  

  问题其实就是从一个点出发,每次可以走与其曼哈顿距离恰好为一个常数\(d\)的点

  

  显然不可能一一走完所有的边,这样复杂度下界至少是\(O(ans)\)

  

  我们采用折中方式:间接统计

  

  (1)找出从起始点能到达哪一些点

  (2)统计对于这些点之中的每一个点,与其距离为d的点有多少,求和除二就是总边数

  

  首先考虑第一步,如果我们通过枚举边的思路进行广搜,又要触及边的数目过多这一限制。考虑距离一个点\((x_0,y_0)\)曼哈顿距离为\(d\)的点\((x,y)\)应该满足什么特征,分四类:左上左下右上右下。左上右下的限制都是形如\(x-y=x_0\pm y_0\pm d\)且\(x\)在一定范围内的点,右上坐下的限制都是形如\(x+y=x_0\pm y_0\pm d\)的点

  

  枚举四种情况时,两个符号可以直接定下来,关键是对于每个\(a\),组织起满足\(x+y=a\)或\(x-y=a\)的点,并按\(x\)大小维护。由于我们不需要统计\(x\)在某个范围内的数具体有多少个,而仅仅是需要迭代枚举功能,我们马上想到内层应该要用一个set维护这一些点。那么外层是一个\(x+y\)或\(x-y\)的索引,关于索引,用map

  

  所以用两个map套set,维护\(x+y\)和\(x-y\)的点的信息。对于一个点,它在两个map中都有且仅有一个存在

  

  那么广搜时只需在map上查询所需的特征对应的set,并确定\(x\)值范围,在set上迭代枚举即可

  

  如果这样,我们发现这个复杂度下界至少是边数。我们第一步是找出所有联通点,而不是统计边数,每个点显然入一次队即可。所以如果一个点入队,我们就把它在两个map中的存在删除。这样就能保证复杂度与点数相关

  

  接下来是第二步,由于是无序边,我们想到顺序往右扫、并单向往左统计连边

  

  对于关键点按\(x\)排序,顺序右扫。考虑先前加入的点与当前点能连多少边。那么我们还是需要用\(x+y\)和\(x-y\)作为两个特征储存先前的点。与(1)不一样的是,这回我们要统计等于某个特征值、\(x\)在某个范围内的点有多少个。这回我们不用set,而用map套vector,这样就可以通过二分来确定某个范围内有多少个点。由于\(x\)递增、我们要查询的范围也和\(x\)有关,所以当前点加入信息时,直接将其pushback到每个vector后即可

  

Code

#include <cstdio>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=100005;
int n,sa,sb;
int stdDis;
struct Point{
int x,y,id;
void read(int _id){
scanf("%d%d",&x,&y);
id=_id;
}
};
Point p[N],a[N];
struct CompairByX{
bool operator() (const int &a,const int &b){
return p[a].x<p[b].x;
}
}tcmp;
map<int,set<int,CompairByX>> keyp,keyd; // x+y/x-y
int acnt;
inline int abs(int x){
return x>=0?x:-x;
}
int calc(Point &a,Point &b){
return abs(a.x-b.x)+abs(a.y-b.y);
}
void readData(){
scanf("%d%d%d",&n,&sa,&sb);
for(int i=1;i<=n;i++)
p[i].read(i);
stdDis=calc(p[sa],p[sb]);
}
void addPoint(Point &a){
keyp[a.x+a.y].insert(a.id);
keyd[a.x-a.y].insert(a.id);
}
void remPoint(Point &a){
keyp[a.x+a.y].erase(a.id);
keyd[a.x-a.y].erase(a.id);
}
void findConnectedSet(){
// store into a[acnt]
acnt=0;
for(int i=1;i<=n;i++)
addPoint(p[i]);
static queue<int> q;
static bool vis[N];
q.push(sa);
vis[sa]=true;
remPoint(p[sa]);
while(!q.empty()){/*{{{*/
int u=q.front(),v,cur;
q.pop();
a[++acnt]=p[u];
cur=p[u].x+p[u].y+stdDis;
for(auto i=keyp[cur].lower_bound(u),j=i;i!=keyp[cur].end()&&p[*i].x<=p[u].x+stdDis;i=j){ //right
j++;
v=(*i);
if(!vis[v]){
vis[v]=true;
q.push(v);
remPoint(p[v]);
}
}
cur=p[u].x+p[u].y-stdDis;
p[n+1]=(Point){p[u].x-stdDis,0};
for(auto i=keyp[cur].lower_bound(n+1),j=i;i!=keyp[cur].end()&&p[*i].x<p[u].x;i=j){ // left
j++;
v=(*i);
if(!vis[v]){
vis[v]=true;
q.push(v);
remPoint(p[v]);
}
}
cur=p[u].x-p[u].y+stdDis;
for(auto i=keyd[cur].lower_bound(u),j=i;i!=keyd[cur].end()&&p[*i].x<=p[u].x+stdDis;i=j){ //right
j++;
v=(*i);
if(!vis[v]){
vis[v]=true;
q.push(v);
remPoint(p[v]);
}
}
cur=p[u].x-p[u].y-stdDis;
for(auto i=keyd[cur].lower_bound(n+1),j=i;i!=keyd[cur].end()&&p[*i].x<p[u].x;i=j){ // left
j++;
v=(*i);
if(!vis[v]){
vis[v]=true;
q.push(v);
remPoint(p[v]);
}
}
}/*}}}*/
}
bool cmpByX(const Point &a,const Point &b){
return a.x<b.x;
}
map<int,vector<int>> xp,xd;
void calcAnswer(){
ll ans=0;
sort(a+1,a+1+acnt,cmpByX);
for(int i=1;i<=acnt;i++){
int cur=a[i].x-a[i].y-stdDis,left;
left=lower_bound(xd[cur].begin(),xd[cur].end(),a[i].x-stdDis)-xd[cur].begin();
ans+=xd[cur].size()-left;
cur=a[i].x+a[i].y-stdDis;
left=lower_bound(xp[cur].begin(),xp[cur].end(),a[i].x-stdDis)-xp[cur].begin();
if(left<xp[cur].size()&&xp[cur][left]==a[i].x-stdDis)
left++;
ans+=xp[cur].size()-left;
xd[a[i].x-a[i].y].pb(a[i].x);
xp[a[i].x+a[i].y].pb(a[i].x);
}
printf("%lld\n",ans);
}
int main(){
readData();
findConnectedSet();
calcAnswer();
return 0;
}

【ARC065E】??的更多相关文章

  1. 【Luogu4931】情侣?给我烧了! 加强版(组合计数)

    [Luogu4931]情侣?给我烧了! 加强版(组合计数) 题面 洛谷 题解 戳这里 忽然发现我自己推的方法是做这题的,也许后面写的那个才是做原题的QwQ. #include<iostream& ...

  2. 【Luogu4921】情侣?给我烧了!(组合计数)

    [Luogu4921]情侣?给我烧了!(组合计数) 题面 洛谷 题解 很有意思的一道题目. 直接容斥?怎么样都要一个平方复杂度了. 既然是恰好\(k\)对,那么我们直接来做: 首先枚举\(k\)对人出 ...

  3. 【5min+】 什么?原来C#还有这两个关键字

    系列介绍 简介 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的. ...

  4. 【iOS】Swift ?和 !(详解)

    Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋初始值, 也就是说变量不会有默认值,所以要求使用变量之前必须要对其初始化 .如果在使用变量之前不进行初始化就会报错: [ ...

  5. 【app】Hybrid?Native?不知道你就out了!

    Hybrid?是个啥? 相信大家在平常生活中也会经常见到这个词,比如现在比较火的hybrid汽车(混合动力汽车) 那如果是针对于App而言呢? 那就要从App的分类说起了 目前主流应用程序大体分为三类 ...

  6. 为什么要用lock 【readonly】object?为什么不要lock(this)?

    一. 为什么要用lock,lock了什么? 当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待.但当不同的线程都需要访问某个资源的时候,就需要同步机制了.也就是说 ...

  7. 【Scratch】编程?一节课就教会你!其实我们不用一个个学习如何使用代码。

    第199篇文章 老丁的课程 在很多教程里面,大家都喜欢把模块拿出来一个个讲述其功能. 这样做的好处是,可以把每个代码模块的功能讲的很清楚.但最最讨厌的问题也随之而来…… 举个例子,当你学习英语的时候, ...

  8. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  9. CodeForces 【20C】Dijkstra?

    解题思路 heap+Dijkstra就能过.注意边是双向边,要用long long. 附上代码 #include <iostream> #include <queue> #in ...

随机推荐

  1. [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子

    [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子$ cat customers.txt 1 Ali us 2 Bsb ca 3 Carls mx $ hive h ...

  2. 爬虫学习--http请求详解

    上篇博客里面写了,爬虫就是发http请求(浏览器里面打开发送的都是http请求),然后获取到response,咱们再从response里面找到想要的数据,存储到本地. 咱们本章就来说一下什么是http ...

  3. Node.js系列-express(下)

    前言 距上次更新博客又两个月多了,这两个月内除了上班时间忙公司的项目外,下班后也没有闲着,做了点外包,有小程序的,管理端的项目.也可能那段时间做的外包项目也都比较急,所以晚上都搞到一点左右睡,严重的压 ...

  4. 计算机网络什么是OSI7层模型、TCP/IP4层模型理解

    模型图解 应用层 就是最顶层的.通常指的应用程序初始走的协议比如有 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层 主要对数据应用层的数据包进行加密 会话层 建立.管理. ...

  5. LVS+Keepalived 高可用环境部署记录(主主和主从模式)

    之前的文章介绍了LVS负载均衡-基础知识梳理, 下面记录下LVS+Keepalived高可用环境部署梳理(主主和主从模式)的操作流程: 一.LVS+Keepalived主从热备的高可用环境部署 1)环 ...

  6. Note: SE Class's Individual Project

    虽然第一个Project还有点小问题需要修改,但是大体已经差不多了,先把blog记在这里,算是开博第一篇吧! 1.项目预计的用时 本来看到这个题的时候想的并不多,但是看了老师的要求才觉得如此麻烦ORZ ...

  7. cglib 动态代理

    JDK的动态代理比较慢,可以使用cglib的代理,速度比较快: package cn.demo02; import java.lang.reflect.Method; import java.util ...

  8. Spring MVC静态资源处理(转)

    原文地址: http://www.cnblogs.com/fangqi/archive/2012/10/28/2743108.html 优雅REST风格的资源URL不希望带 .html 或 .do 等 ...

  9. 4-Python3从入门到实战—基础之数据类型(字符串-String)

    Python从入门到实战系列--目录 字符串表示 在 Python 3版本中,字符串是以 Unicode 编码的:Python 中使用 ' '或者" "表示字符串 msg = 'H ...

  10. 如何批量删除Docker中已经停止的容器

    如何批量删除Docker中已经停止的容器   方法一: #显示所有的容器,过滤出Exited状态的容器,取出这些容器的ID, sudo docker ps -a|grep Exited|awk '{p ...