zoukankan      html  css  js  c++  java
  • Arc065_E Manhattan Compass

    平面上有$N$个点$(X_ispace, Y_i)$,定义$D(a,b)=|X_a-X_b|+|Y_a-Y_b|$。

    如果你当前在$(p,q)$,这个无序二元组(即$(p,q)$和$(q,p)$被认为是相同的),并且存在$r$使得$D(p, q)=D(p, r)$,那么你就可以到达$(p, r)$这个无序二元组。

    一开始你在$(a,b)$,这个无序二元组,求你能到达多少不同的无序二元组。

     

    题解

    这道题其实没有任何大的思维跨越,大体思想就是先求出$(a,b)$的曼哈顿距离,然后$bfs$看可以连出去多少条边即可。

    然而这样朴素的做法是$O(N^2)$的,想办法优化这一过程。

    显然枚举出发点不能进行深一步的优化,所以我们考虑优化枚举到达点的方法。

    不难发现,从固定曼哈顿距离确定每一个点出发到达的点一定在一个斜$45$度的正方形上,可以对于每一条正反斜$45$度的直线开一个$vector$按$x$升序存下内部的点,这样就可以在$vector$上进行二分。

    至于如何将这些点依次加入队列,可以直接开一个并查集维护$vector$中右侧最近的没有被删除的点,由于每个点只会被删除一次,所以总复杂度$O(N)$

    考虑到每次在$vector$上找点还需要二分,所以常数较大,但复杂度仍是$O(Nlog N)$

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define LL long long
    #define M 100020
    using namespace std;
    int read(){
    	int nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    int n,m,cnt,A,B,q[M],hd,tl,D,X[M],Y[M];
    LL ans;
    bool vis[M];
    vector<int> t[M<<1],f[M<<1];
    map<int,int> ID1,ID2;
    int fd(int kd,int x){return f[kd][x]==x?x:f[kd][x]=fd(kd,f[kd][x]);}
    bool cmp(int x,int y){return X[x]<X[y];}
    void gtin(int x){
    	int tar; X[x]=read(),Y[x]=read();
    	if(ID1.count(X[x]+Y[x])) tar=ID1[X[x]+Y[x]];
    	else cnt++,tar=ID1[X[x]+Y[x]]=cnt,f[tar].push_back(0);
    	t[tar].push_back(x),f[tar].push_back(f[tar].size());
    	if(ID2.count(X[x]-Y[x])) tar=ID2[X[x]-Y[x]];
    	else cnt++,tar=ID2[X[x]-Y[x]]=cnt,f[tar].push_back(0);
    	t[tar].push_back(x),f[tar].push_back(f[tar].size());
    }
    int getpos(int kd,int minx){
    	int l=0,r=t[kd].size(),fin=0,md;
    	while(l<=r){
    		md=((l+r)>>1);
    		if(md<t[kd].size()&&X[t[kd][md]]<minx) l=md+1;
    		else fin=md,r=md-1;
    	} return fin;
    }
    void del(int x){
    	vis[x]=true; q[tl++]=x;
    	int kd=ID1[X[x]+Y[x]],fin; fin=getpos(kd,X[x]);
    	f[kd][fin]=fd(kd,fin+1),kd=ID2[X[x]-Y[x]];
    	fin=getpos(kd,X[x]),f[kd][fin]=fd(kd,fin+1);
    }
    void calc(int kd,int lx,int rx,bool down){
    	int t1=getpos(kd,lx),t2=getpos(kd,rx),tot=t[kd].size();
    	if(down&&t1<tot&&X[t[kd][t1]]<=lx) t1++;
    	if(down&&t2<tot&&X[t[kd][t2]]<=rx) t2++; ans+=t2-t1;
    	for(t1=fd(kd,t1);t1<t2&&t1<tot;t1=fd(kd,t1+1)) del(t[kd][t1]);
    	
    }
    void getans(int x){
    	if(ID2.count(X[x]-Y[x]-D)) calc(ID2[X[x]-Y[x]-D],X[x]-D,X[x],false);
    	if(ID1.count(X[x]+Y[x]+D)) calc(ID1[X[x]+Y[x]+D],X[x],X[x]+D,false);
    	if(ID1.count(X[x]+Y[x]-D)) calc(ID1[X[x]+Y[x]-D],X[x]-D,X[x],true);
    	if(ID2.count(X[x]-Y[x]+D)) calc(ID2[X[x]-Y[x]+D],X[x],X[x]+D,true);
    }
    void solve(){
    	del(A),del(B),D=abs(X[A]-X[B])+abs(Y[A]-Y[B]);
    	while(hd<tl){int now=q[hd++];getans(now);}
    }
    int main(){
    	n=read(),A=read(),B=read();
    	for(int i=1;i<=n;i++) gtin(i);
    	for(int i=1;i<=cnt;i++) sort(t[i].begin(),t[i].end(),cmp);
    	solve(),printf("%lld
    ",ans/2); return 0;
    }
    

      

  • 相关阅读:
    异常之【You have an error in your SQL syntax】
    Android 回调的理解,觉得写得好就转过来。。。收藏一下
    git 解决冲突方法
    最重要的“快捷键” IntelliJ IDEA
    Git 还没push 前可以做的事(转)
    android JNI(转)
    Windos下Android(ADT Bundle)配置NDK的两种方法------ADT、Cygwin、NDK配置汇总(转)
    warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
    android-ndk-r7b编译环境Cygwin工具搭建及配置(转)
    Eclipse Java常用快捷键(Eclipse Shortcut Keys for Java Top10)(转)
  • 原文地址:https://www.cnblogs.com/OYJason/p/9666890.html
Copyright © 2011-2022 走看看