【USACO 2008 Open Gold】 3.Cow Neighborhoods 平衡树、并查集

题解:

首先曼哈顿距离有些不好维护,但是它可以转化:

一个点本来的坐标是(x,y),那么可以转化成(x+y,x-y)

这样就人为构造出一种性质:1、2两点曼哈顿距离=max(|x1-x2|,|y1-y2|);


这样我们就可以排序单调搞掉一维,然后另一维只需要求前驱后继到该点的距离

满足则加并查集。


这个过程可以用权值线段树,也可以用平衡树。但是权值线段树还需要离散化,反而代码多了。


代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define ls son[x][0]
#define rs son[x][1]
#define is(x) (x==son[fa[x]][1])
#define inf 0x3f3f3f3f
using namespace std;
struct Point
{
	int x,y;
	bool operator < (const Point &a)const{return x==a.x?y<a.y:x<a.x;}
}p[N];
int n,m;
int f[N];int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
struct SPT
{
	int son[N][2],fa[N],root,cnt;
	int val[N],id[N],size[N];
	void pushup(int x)
	{
		size[x]=size[ls]+size[rs]+1;
	}
	void link(int x,int y,int d){son[y][d]=x,fa[x]=y;}
	void rotate(int x)
	{
		int y=fa[x],z=fa[y],i=is(x),t=son[x][!i];
		link(t,y,i),link(x,z,is(y)),link(y,x,!i);
		fa[0]=0;
		pushup(y);
	}
	void splay(int x,int k=0)
	{
		int y,z;
		while(fa[x]!=k)
		{
			y=fa[x],z=fa[y];
			if(z==k){rotate(x);break;}
			rotate(is(x)==is(y)?y:x),rotate(x);
		}
		pushup(x);
		if(!k)root=x;
		return ;
	}
	void newnode(int &x,int y,int w,int p)
	{
		x=p;
		fa[p]=y,ls=rs=0;
		val[p]=w,size[p]=1;
	}
	void cls()
	{
		root=fa[cnt=2]=1,son[1][1]=2;
		val[1]=-inf,val[2]=inf;
		size[1]=2,size[2]=1;
	}
	int pred(int x,int k=0)
	{
		splay(x);
		for(x=ls;rs;)x=rs;
		splay(x,k);
		return x;
	}
	int succ(int x,int k=0)
	{
		splay(x);
		for(x=rs;ls;)x=ls;
		splay(x,k);
		return x;
	}
	void remove(int x)
	{
		pred(x);
		splay(x,root);
		link(rs,root,1);
	}
	void insert(int w)
	{
		int x=root;
		for(;son[x][val[x]<w];x=son[x][val[x]<w]);
		newnode(son[x][val[x]<w],x,w,++cnt);
		splay(son[x][val[x]<w]);
	}
	void solve(int w)
	{
		insert(w);
		int pr=pred(cnt),su=succ(cnt);
		if(abs(val[pr]-w)<=m)f[find(pr)]=cnt;
		if(abs(val[su]-w)<=m)f[find(su)]=cnt;
	}
}spt;
int num[N],nn,ans;
int main()
{
	int i,j,k;
	scanf("%d%d",&n,&m);
	if(m<=0)
	{
		printf("%d 1\n",n);
		return 0;
	}
	for(i=1;i<=n;i++)scanf("%d%d",&j,&k),p[i].x=j+k,p[i].y=j-k;
	for(i=3;i<=n+2;i++)f[i]=i;
	sort(p+1,p+n+1);
	spt.cls();
	int l=1,r;
	for(r=1;r<=n;r++)
	{
		while(p[r].x-p[l].x>m)spt.remove(2+l++);
		spt.solve(p[r].y);
	}
	for(i=3;i<=n+2;i++)num[find(i)]++;
	for(i=3;i<=n+2;i++)if(num[i])nn++,ans=max(ans,num[i]);
	printf("%d %d\n",nn,ans);
	return 0;
}


你可能感兴趣的:(USACO,open,2008,并查集,COW,Gold,平衡树,Neighborhoods,前驱后继)