【bzoj1604】【Usaco2008 Open】Cow Neighborhoods (set+曼哈顿距离性质+并查集)奶牛的邻居

本文介绍了一种使用曼哈顿距离特性及Set数据结构解决特定问题的方法。通过坐标转换,利用Set维护可能相邻的点,实现了快速查找与合并同一区域内的点,适用于算法竞赛中的相似题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1604
题解:
首先要做这道题我们知道曼哈顿距离的一个性质,那就是:
如果原来坐标是(x, y),令新的坐标为(X, Y), 其中X = x + y, Y = x - y
曼哈顿距离 = |x1 - x2| + |y1 - y2| = max ( |X1 - X2|, |Y1 - Y2| );
还有就是如何运用set,这个百度里可以搜到,看这个东西大概可以知道set怎么用
http://baike.baidu.com/link?url=uVFabYTc3qYwwhKfxKuS-vfAFSKYQquK8iEq4vVrRXyFDm8Ps9oH_GADc6Lfs40cTLfbjfXhnMVLRL-NtnnN4q
接下来就是这道题的关键点,如何判断哪些点在同一个块中:
首先我们将给出的每个点的坐标用(x+y , x-y)来表示,这样的话按新的x坐标从小到大排序,维护一个队列满足队首到队尾的X坐标(X=x+y)距离小于c,这样的话 max ( |X1 - X2|, |Y1 - Y2| ) 中 |X1 - X2| 就满足小于c了,如何满足 |Y1 - Y2| 也小于c呢,我们需要用到平衡树即set,如果新加入元素在set中的前驱后继与它的Y值差值不超过c,则用并查集将他们连在一起,set本身有好多用法,可以参照上面给出的网页。
代码:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<set>
using namespace std;
typedef long long ll;
const ll inf=1e16;
struct node{
    ll x;ll y;int id;
}a[100005];
int c,n,fa[100005],cnt[100005];
multiset<node>s;
node jia(ll xx,ll yy,int idd)
{
    node tmp;
    tmp.x=xx;tmp.y=yy;tmp.id=idd;
    return tmp;
}
int cmp(node x,node y)
{
    if (x.x!=y.x)
    return x.x<y.x;
    return x.y<y.y;
}
bool operator < (const node a, const node b)
{
    return a.y < b.y;
}
int find(int x)
{
    if (fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
int num;
void unio(int x,int y)
{
    //cout<<x<<' '<<y<<endl;
    int X=find(x);
    int Y=find(y);
    if (X!=Y)
    {
        fa[X]=Y;
        num--;
    }
}
int main()
{
    scanf("%d%d",&n,&c);num=n;
    for (int i=1;i<=n;i++)
    {
        ll x,y;
        fa[i]=i;
        scanf("%lld%lld",&x,&y);
        a[i].x=x+y;a[i].y=x-y;
        a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    s.insert(jia(0,inf,0));
    s.insert(jia(0,-inf,0));
    s.insert(a[1]);
    int head=1;
    node l,r;
    for (int i=2;i<=n;i++)
    {
        while(a[i].x-a[head].x>c)
        {
            s.erase(s.find(a[head]));
            head++;
        }
        set <node> ::iterator it=s.lower_bound(a[i]);
        r=*it;it--;l=*it;
        if (a[i].y-l.y<=c)
        unio(a[i].id,l.id);
        if (r.y-a[i].y<=c)
        unio(a[i].id,r.id);
        s.insert(a[i]);
    }
    int sum=-1;
    for (int i=1;i<=n;i++)
    {
        int tmp=find(i);
        cnt[tmp]++;
        sum=max(sum,cnt[tmp]);
    }
    printf("%d %d\n",num,sum);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值