bzoj1604 [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居 (曼哈顿距离 转 切比雪夫距离+并查集+set)

bzoj1604 [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居

权限题贴下题面。

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=1604

题意:
了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个“群”.每只奶牛在吃草的时候有一个独一无二的位置坐标Xi,Yi(l≤Xi,Yi≤[1..10^9];Xi,Yi∈整数.当满足下列两个条件之一,两只奶牛i和j是属于同一个群的:
1.两只奶牛的曼哈顿距离不超过C(1≤C≤10^9),即lXi - xil+IYi - Yil≤C.
2.两只奶牛有共同的邻居.即,存在一只奶牛k,使i与k,j与k均同属一个群.
给出奶牛们的位置,请计算草原上有多少个牛群,以及最大的牛群里有多少奶牛
Input

第1行输入N和C,之后N行每行输入一只奶牛的坐标.

Output

仅一行,先输出牛群数,再输出最大牛群里的牛数,用空格隔开.

Sample Input
4 2
1 1
3 3
2 2
10 10

Line 1: A single line with a two space-separated integers: the
number of cow neighborhoods and the size of the largest cow
neighborhood.

Sample Output
2 3

OUTPUT DETAILS:

There are 2 neighborhoods, one formed by the first three cows and the other being the last cow. The largest neighborhood therefore has size 3.

数据范围
1≤N≤100000 ,Xi,Yi(l≤Xi,Yi≤[1..10^9],Xi,Yi∈整数
1≤C≤10^9

题解:
对于给出的曼哈顿距离的形式:|Xi - xi|+|Yi - yi|≤C.
绝对值的和,这样的形式很不好处理,这里有一个相当经典的转化方式:
曼哈顿距离 转 切比雪夫距离
对于两个点(x1,y1),(x2,y2),他们的曼哈顿距离|x1 - x2|+|y1- y2|<=c

 -c<=x1 - x2+y1-y2<=c   (1)
 -c<=x1 - x2-y1+y2<=c   (2)
可以转化为:
 -c<=(x1+y1)-(x2+y2)<=c   (1)
 -c<=(x1- y1)-(x2- y2)<=c   (2)
我们令X=x+y,Y=x-y。
原来的转化成了:
 |X1-X2|≤C  (1)
 |Y1-Y2|≤C   (2)
于是转化成了更好处理的切比雪夫距离。
输入时我们就进行处理,将x+y作为X坐标存储,x-y作为Y坐标存储。
因此两个点直接相连当且仅当他们的X坐标之差<=c且Y坐标之差<=c,而间接相连显然是通过直接相连,因此我们考虑将直接相连的点加入并查集。

这里给出一种算法:
把所有点按x坐标排序,依次从每个点开始朝一个方向维护一个x坐标之差<=c的队列,加入到按y坐标排序的set中,每加进一个点,在set中查找其前后驱,如果前/后驱与他的y之差<=c就把他和他前/后驱加进并查集。

这里给出一个粗浅的证明:
例如i点坐标(x,y)(转化后的),有一些x坐标小于x的点与i直接相连,我们只需把y坐标第一个小于和大于他的点与他加入并查集,由于这些点与他的x坐标之差,y坐标之差都<=c,他们都是直接相连的,只需对每个点都这样只连y坐标之差最近的两个点,就像一条链一样,即可保证所有这些点都加入了一个并查集。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int N=100005;
int n,c,fa[N],size[N];
const int inf=1e9+10;
struct node
{
    int x,y;
    node(){}
    node(int x,int y):x(x),y(y){}
    bool friend operator<(const node &A,const node &B)
    {
        return A.y<B.y;
    }
    bool friend operator==(const node &A,const node &B)
    {
        return (A.x==B.x)&&(A.y==B.y);
    }
}p[N];
bool cmp(const node &A,const node &B)
{
    return A.x<B.x;
}
int getfa(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=getfa(fa[x]);
}
bool cmpy(int &A,int &B)
{
    return p[A].y<p[B].y;
}
void merge(int x,int y)
{
    int fx=getfa(x); int fy=getfa(y);
    if(fx==fy) return;  
    fa[fy]=fx;
}
set<pair<int,int> > S;

int main()
{
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++) fa[i]=i; 
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        p[i].x=x+y; p[i].y=x-y;
    }
    sort(p+1,p+n+1,cmp);
    S.insert(make_pair(-inf,-inf)); S.insert(make_pair(inf,inf));
    int lf=1;
    S.insert(make_pair(p[1].y,1));
    for(int i=2;i<=n;i++)
    {
        while(p[i].x-p[lf].x>c)
        {S.erase(make_pair(p[lf].y,lf));lf++;}
        S.insert(make_pair(p[i].y,i));
        pair<int,int> pre=*--S.find(make_pair(p[i].y,i));
        pair<int,int> nxt=*++S.find(make_pair(p[i].y,i));
        if(pre.second!=-inf&&p[i].y-pre.first<=c) merge(i,pre.second);
        if(nxt.second!=inf&&nxt.first-p[i].y<=c) merge(i,nxt.second);       
    }
    int cnt=0; int mx=-1;
    for(int i=1;i<=n;i++)
    {
        int fx=getfa(i);
        size[fx]++; if(fx==i) cnt++;
    }
    for(int i=1;i<=n;i++) mx=max(mx,size[i]);
    printf("%d %d\n",cnt,mx);
    return 0;
}

我最开始是顺着写的,因为忽略了可能最后出现j

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int N=100005;
int n,c,fa[N],size[N];
const int inf=1e9+10;
struct node
{
    int x,y;
    node(){}
    node(int x,int y):x(x),y(y){}
    bool friend operator<(const node &A,const node &B)
    {
        return A.y<B.y;
    }
    bool friend operator==(const node &A,const node &B)
    {
        return (A.x==B.x)&&(A.y==B.y);
    }
}p[N];
bool cmp(const node &A,const node &B)
{
    return A.x<B.x;
}
int getfa(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=getfa(fa[x]);
}
bool cmpy(int &A,int &B)
{
    return p[A].y<p[B].y;
}
void merge(int x,int y)
{
    int fx=getfa(x); int fy=getfa(y);
    if(fx==fy) return;  
    fa[fy]=fx;
}
set<pair<int,int> > S;

int main()
{
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++) fa[i]=i; 
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        p[i].x=x+y; p[i].y=x-y;
    }
    sort(p+1,p+n+1,cmp);
    S.insert(make_pair(-inf,-inf)); S.insert(make_pair(inf,inf));
    int i=1,j=1;
    for(i=1;i<=n;i++)
    {
        S.insert(make_pair(p[i].y,i));
        while((j<n&&p[j+1].x-p[i].x<=c))
        {
            j++;
            S.insert(make_pair(p[j].y,j));
            pair<int,int> pre=*--S.find(make_pair(p[j].y,j));
            pair<int,int> nxt=*++S.find(make_pair(p[j].y,j));
            if(pre.second!=-inf&&p[j].y-pre.first<=c) merge(j,pre.second);
            if(nxt.second!=inf&&nxt.first-p[j].y<=c) merge(j,nxt.second);
        }
        S.erase(make_pair(p[i].y,i));
    }

    int cnt=0; int mx=-1;
    for(int i=1;i<=n;i++)
    {
        int fx=getfa(i);
        size[fx]++; if(fx==i) cnt++;
    }
    for(int i=1;i<=n;i++) mx=max(mx,size[i]);
    printf("%d %d\n",cnt,mx);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值