bzoj1604:奶牛的邻居(treap)

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

Description

了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个.每只奶牛在吃草的时候有一个独一无二的位置坐标XiYi(l≤XiYi≤[1.10^9]XiYi整数.当满足下列两个条件之一,两只奶牛ij是属于同一个群的:

  1.两只奶牛的曼哈顿距离不超过C(1≤C≤10^9),即lXi – xil+IYi – Yil≤C.

  2.两只奶牛有共同的邻居.即,存在一只奶牛k,使ikjk均同属一个群.

    给出奶牛们的位置,请计算草原上有多少个牛群,以及最大的牛群里有多少奶牛

Input

   1行输入NC,之后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 3OUTPUT 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.

题目分析:曼哈顿距离的一种巧妙转化:令A[i]=x[i]+y[i],B[i]=x[i]-y[i],abs(x[i]-x[j])+abs(y[i]-y[j])=max(abs(A[i]-A[j]),abs(B[i]-B[j]))。这样我们就把两个abs相加转变为了两个abs取max,它们是独立的了。于是我们用排序搞定其中一维,然后从左到右扫,用treap维护即可。我们每处理一个点,只需要将A小于等于它且差值不超过c的点的B值加进treap里(为什么不需要考虑A值比它大的?因为两个点i,j,一定有一个被先处理到,我们只要由后处理到的向先处理到的连边就不会重复遗漏),然后和它的前驱后继连边(因为后继的后继已经和后继连了边,就不需要再由它向后继连边了)。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
#include<ctime>
using namespace std;

const int maxn=100100;
const int oo=2000000001;

struct Tnode
{
	int val,fix;
	int id,Size;
	Tnode *lson,*rson;
} tree[maxn];
Tnode *root=NULL;
int cur=-1;

struct data
{
	int x,y;
} cow[maxn];

int fa[maxn];
int _Size[maxn];
bool Root[maxn];

int n,c;

bool Comp(data p,data q)
{
	return p.x<q.x;
}

Tnode *New_node(int nid,int v)
{
	cur++;
	tree[cur].val=v;
	tree[cur].fix=rand();
	tree[cur].id=nid;
	tree[cur].Size=1;
	tree[cur].lson=tree[cur].rson=NULL;
	return tree+cur;
}

void Recount(Tnode *&P)
{
	int temp=1;
	if (P->lson) temp+=P->lson->Size;
	if (P->rson) temp+=P->rson->Size;
	P->Size=temp;
}

void Right_turn(Tnode *&P)
{
	Tnode *W=P->lson;
	P->lson=W->rson;
	W->rson=P;
	P=W;
	Recount(P->rson);
	Recount(P);
}

void Left_turn(Tnode *&P)
{
	Tnode *W=P->rson;
	P->rson=W->lson;
	W->lson=P;
	P=W;
	Recount(P->lson);
	Recount(P);
}

void Insert(Tnode *&P,int nid,int v)
{
	if (!P) P=New_node(nid,v);
	else if ( v<P->val || ( v==P->val && nid<P->id ) )
		 {
		 	 Insert(P->lson,nid,v);
		 	 if ( P->lson->fix < P->fix ) Right_turn(P);
		 }
		 else
		 {
		 	Insert(P->rson,nid,v);
		 	if ( P->rson->fix < P->fix ) Left_turn(P);
		 }
	Recount(P);
}

void Delete(Tnode *&P,int nid,int v)
{
	if ( v==P->val && nid==P->id )
	{
		if ( P->lson && P->rson )
		{
			if ( P->lson->fix < P->rson->fix )
			{
				Right_turn(P);
				Delete(P->rson,nid,v);
			}
			else
			{
				Left_turn(P);
				Delete(P->lson,nid,v);
			}
			Recount(P);
		}
		else if (P->lson) P=P->lson;
			 else if (P->rson) P=P->rson;
			 	  else P=NULL;
	}
	else
	{
		if ( v<P->val || ( v==P->val && nid<P->id ) ) Delete(P->lson,nid,v);
		else Delete(P->rson,nid,v);
		Recount(P);
	}
}

int Get_rank(Tnode *P,int nid,int v)
{
	int left_size=0;
	if (P->lson) left_size=P->lson->Size;
	if ( v==P->val && nid==P->id ) return left_size+1;
	else if ( v<P->val || ( v==P->val && nid<P->id ) ) return Get_rank(P->lson,nid,v);
		 else return left_size+1+Get_rank(P->rson,nid,v);
}

int Abs(int v)
{
	if (v>=0) return v;
	return -v;
}

int Query(Tnode *P,int rank,int v)
{
	if ( rank==0 || rank>P->Size ) return 0;
	int left_size=0;
	if (P->lson) left_size=P->lson->Size;
	if (rank<=left_size) return Query(P->lson,rank,v);
	else if (rank==left_size+1)
		 {
		 	if ( Abs(v-P->val)<=c ) return P->id;
		 	else return 0;
		 }
		 else return Query(P->rson,rank-left_size-1,v);
}

void Up(int p)
{
	if (p==fa[p]) return;
	Up(fa[p]);
	fa[p]=fa[ fa[p] ];
}

void Add(int p,int q)
{
	Up(p);
	Up(q);
	if (fa[p]==fa[q]) return;
	_Size[ fa[q] ]+=_Size[ fa[p] ];
	fa[ fa[p] ]=fa[q];
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	
	srand( time(0) );
	
	scanf("%d%d",&n,&c);
	for (int i=1; i<=n; i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		cow[i].x=a+b;
		cow[i].y=a-b;
	}
	
	sort(cow+1,cow+n+1,Comp);
	for (int i=1; i<=n; i++)
	{
		fa[i]=i;
		_Size[i]=1;
	}
	
	int temp=1;
	Insert(root,1,cow[1].y);
	for (int i=2; i<=n; i++)
	{
		Insert(root,i,cow[i].y);
		while (cow[i].x-cow[temp].x>c)
		{
			Delete(root,temp,cow[temp].y);
			temp++;
		}
		int rank=Get_rank(root,i,cow[i].y);
		int last=Query(root,rank-1,cow[i].y);
		int Next=Query(root,rank+1,cow[i].y);
		if (last) Add(last,i);
		if (Next) Add(Next,i);
	}
	
	for (int i=1; i<=n; i++) Up(i);
	for (int i=1; i<=n; i++) Root[ fa[i] ]=true;
	int num=0,max_size=0;
	for (int i=1; i<=n; i++) if (Root[i])
	{
		num++;
		max_size=max(max_size,_Size[i]);
	}
	printf("%d %d\n",num,max_size);
	
	//for (int i=1; i<=n; i++) printf("%d ",fa[i]);
	//printf("\n");
	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值