BZOJ 1604 [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居:队列 + multiset + 并查集【曼哈顿距离变形】...

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1604

题意:

  平面直角坐标系中,有n个点(n <= 100000,坐标范围10^9)。

  给定r,当两个点的曼哈顿距离<=r时,认为这两个点在同一个“群”中。

  问你共有多少个群,以及点的数量最多的群有多少个点。

 

题解:

  本题的核心在于:如何枚举一个点周围满足“曼哈顿距离<=r”的点。

  由于 曼哈顿距离 = |x1 - x2| + |y1 - y2|。

  x和y相互影响,不能单纯按x或y排序,枚举所有点总复杂度为O(N^2)。

 

  所以要用到曼哈顿距离的另一种形式:

    设X = x + y , Y = x - y

    d(曼哈顿距离) = max(|X1-X2|, |Y1-Y2|)

  将每个点的X = x + y,Y = x - y,这就将X与Y的关系分离开了。

  

  将所有点按X排序。

  当前考虑到点i。

  用一个队列(X升序),保证队列中的所有X满足要求,否则不断删去队首。

  用一个multiset(Y升序),找到i的前驱pre和后继suc,如果i与pre(或suc)的Y满足要求,则合并(并查集)。

 

  最后统计一下每个群就好了。

 

AC Code:

  1 // |x1-x2| + |y1-y2|
  2 // | (x1+y1) - (x2+y2) |
  3 // | (x1-y1) - (x2-y2) |
  4 // X = x + y
  5 // Y = x - y
  6 // d = max(|X1-X2|, |Y1-Y2|) <= r
  7 #include <iostream>
  8 #include <stdio.h>
  9 #include <string.h>
 10 #include <algorithm>
 11 #include <set>
 12 #define MAX_N 100005
 13 #define INF 100000000
 14 
 15 using namespace std;
 16 
 17 struct Coor
 18 {
 19     int x;
 20     int y;
 21     int idx;
 22     Coor(int _x,int _y,int _idx)
 23     {
 24         x=_x;
 25         y=_y;
 26         idx=_idx;
 27     }
 28     Coor(){}
 29     friend bool operator < (const Coor &a,const Coor &b)
 30     {
 31         return a.y!=b.y?a.y<b.y:a.idx<b.idx;
 32     }
 33 };
 34 
 35 int n,r;
 36 int ans=1;
 37 int counter=0;
 38 int head=0;
 39 int par[MAX_N];
 40 int cnt[MAX_N];
 41 Coor c[MAX_N];
 42 multiset<Coor> mst;
 43 
 44 inline bool cmp_x(const Coor &a,const Coor &b)
 45 {
 46     return a.x<b.x;
 47 }
 48 
 49 void init_union_find()
 50 {
 51     for(int i=0;i<n;i++)
 52     {
 53         par[i]=i;
 54     }
 55 }
 56 
 57 int find(int x)
 58 {
 59     return par[x]==x?x:par[x]=find(par[x]);
 60 }
 61 
 62 void unite(int x,int y)
 63 {
 64     int px=find(x);
 65     int py=find(y);
 66     if(px==py) return;
 67     par[px]=py;
 68 }
 69 
 70 void read()
 71 {
 72     cin>>n>>r;
 73     int a,b;
 74     for(int i=0;i<n;i++)
 75     {
 76         cin>>a>>b;
 77         c[i].x=a+b;
 78         c[i].y=a-b;
 79         c[i].idx=i;
 80     }
 81 }
 82 
 83 void solve()
 84 {
 85     init_union_find();
 86     sort(c,c+n,cmp_x);
 87     mst.insert(Coor(0,INF,0));
 88     mst.insert(Coor(0,-INF,0));
 89     mst.insert(c[head]);
 90     for(int i=1;i<n;i++)
 91     {
 92         while(c[i].x-c[head].x>r)
 93         {
 94             mst.erase(c[head]);
 95             head++;
 96         }
 97         multiset<Coor>::iterator it=mst.lower_bound(c[i]);
 98         Coor suc=*it;
 99         Coor pre=*--it;
100         if(c[i].y-pre.y<=r) unite(pre.idx,c[i].idx);
101         if(suc.y-c[i].y<=r) unite(suc.idx,c[i].idx);
102         mst.insert(c[i]);
103     }
104     memset(cnt,0,sizeof(cnt));
105     for(int i=0;i<n;i++)
106     {
107         int p=find(i);
108         if(cnt[p]==0) counter++;
109         cnt[p]++;
110         ans=max(ans,cnt[p]);
111     }
112 }
113 
114 void print()
115 {
116     cout<<counter<<" "<<ans<<endl;
117 }
118 
119 int main()
120 {
121     read();
122     solve();
123     print();
124 }

 

转载于:https://www.cnblogs.com/Leohh/p/7599009.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值