1. 程式人生 > >【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的鄰居 並查集+Treap/STL-set

【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的鄰居 並查集+Treap/STL-set

多少 aps stl oot 絕對值 cmp rac 維護 屬於

題目描述

了解奶牛們的人都知道,奶牛喜歡成群結隊.觀察約翰的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均同屬一個群. 給出奶牛們的位置,請計算草原上有多少個牛群,以及最大的牛群裏有多少奶牛

輸入

1行輸入N和C,之後N行每行輸入一只奶牛的坐標.

輸出

僅一行,先輸出牛群數,再輸出最大牛群裏的牛數,用空格隔開.

樣例輸入

4 2
1 1
3 3
2 2
10 10

樣例輸出

2 3


題解

為了練習Treap找到的這道略神的題

首先直接處理曼哈頓距離不是特別容易,我們可以把所有的點繞著原點逆時針旋轉45°,這樣原來的點$(x,y)$就變為了$(\frac{x-y}{\sqrt 2},\frac{x+y}{\sqrt 2})$,查詢的區域變為了矩形範圍,切比雪夫距離(橫縱坐標差的絕對值最大值)不超過$\frac c{\sqrt 2}$。

然後約掉$\frac 1{\sqrt 2}$,就變為普通的矩形區域查詢問題。

先將所有變換後的點按照橫坐標排序,然後從左往右掃,將左面橫坐標不滿足條件的點刪除。然後考慮連邊:我們沒有必要將所有在範圍之內的點與當前點連邊,只需要將當前點與第一個縱坐標比它大的點、第一個縱坐標比它小的點,如果滿足條件就連邊。

證明:使用數學歸納法

兩個點之間使用這種方法是一定能夠連上的。

如果k個點連上了,且縱坐標都比當前點大,並且橫坐標滿足條件,如果這種方法是不成立的,那麽不妨設y1、y2,其中y1為縱坐標最接近當前點,y2為要連的點,我們要證的就是“當前點與y2有邊,與y1沒有邊”是假命題。證明顯然~

縱坐標比當前點小的時候同理。

於是k+1個點也能連上。命題得證。

回到題中,刪點加點、查詢前驅後繼可以使用平衡樹,維護連通性可以使用並查集。最後掃一遍每個點即可得到答案。

時間復雜度$O(n\log n)$。

事實上,STL的set比Treap還快~

Treap:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N 100010
using namespace std;
struct data
{
	int x , y;
}a[N];
typedef pair<int , int> pr;
int l[N] , r[N] , rnd[N] , tot , root , f[N] , tmp , num[N];
pr w[N];
bool cmp(data a , data b)
{
	return a.x < b.x;
}
void zig(int &k)
{
	int t = l[k];
	l[k] = r[t] , r[t] = k , k = t;
}
void zag(int &k)
{
	int t = r[k];
	r[k] = l[t] , l[t] = k , k = t;
}
void insert(int &k , pr x)
{
	if(!k) k = ++tot , w[k] = x , rnd[k] = rand();
	else if(x < w[k])
	{
		insert(l[k] , x);
		if(rnd[l[k]] < rnd[k]) zig(k);
	}
	else
	{
		insert(r[k] , x);
		if(rnd[r[k]] < rnd[k]) zag(k);
	}
}
void del(int &k , pr x)
{
	if(x == w[k])
	{
		if(!l[k] || !r[k]) k = l[k] + r[k];
		else if(rnd[l[k]] < rnd[k]) zig(k) , del(r[k] , x);
		else zag(k) , del(l[k] , x);
	}
	else if(x < w[k]) del(l[k] , x);
	else del(r[k] , x);
}
void pre(int k , pr x)
{
	if(!k) return;
	else if(x < w[k]) pre(l[k] , x);
	else tmp = w[k].second , pre(r[k] , x);
}
void sub(int k , pr x)
{
	if(!k) return;
	else if(x < w[k]) tmp = w[k].second , sub(l[k] , x);
	else sub(r[k] , x);
}
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
	int n , c , i , u , v , p = 1 , ans = 0 , mx = 0;
	scanf("%d%d" , &n , &c);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &u , &v) , a[i].x = u - v , a[i].y = u + v , f[i] = i;
	sort(a + 1 , a + n + 1 , cmp);
	for(i = 1 ; i <= n ; i ++ )
	{
		while(p < i && a[i].x - a[p].x > c) del(root , pr(a[p].y , p)) , p ++ ;
		tmp = 0 , pre(root , pr(a[i].y , i));
		if(tmp && a[i].y - a[tmp].y <= c) f[find(i)] = find(tmp);
		tmp = 0 , sub(root , pr(a[i].y , i));
		if(tmp && a[tmp].y - a[i].y <= c) f[find(i)] = find(tmp);
		insert(root , pr(a[i].y , i));
	}
	for(i = 1 ; i <= n ; i ++ ) num[find(i)] ++ ;
	for(i = 1 ; i <= n ; i ++ )
		if(num[i])
			ans ++ , mx = max(mx , num[i]);
	printf("%d %d\n" , ans , mx);
	return 0;
}

STL-set:

#include <cstdio>
#include <algorithm>
#include <set>
#define N 100010
using namespace std;
struct data
{
    int x , y;
}a[N];
typedef pair<int , int> pr;
set<pr> s;
set<pr>::iterator it;
int f[N] , num[N];
bool cmp(data a , data b)
{
    return a.x < b.x;
}
int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
    int n , c , i , u , v , p = 1 , ans = 0 , mx = 0;
    scanf("%d%d" , &n , &c);
    for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &u , &v) , a[i].x = u - v , a[i].y = u + v , f[i] = i;
    sort(a + 1 , a + n + 1 , cmp);
    for(i = 1 ; i <= n ; i ++ )
    {
        while(p < i && a[i].x - a[p].x > c) s.erase(pr(a[p].y , p)) , p ++ ;
        it = s.upper_bound(pr(a[i].y , i));
        if(it != s.end() && it->first - a[i].y <= c) f[find(i)] = find(it->second);
        if(it != s.begin() && a[i].y - (--it)->first <= c) f[find(i)] = find(it->second);
        s.insert(pr(a[i].y , i));
    }
    for(i = 1 ; i <= n ; i ++ ) num[find(i)] ++ ;
    for(i = 1 ; i <= n ; i ++ )
        if(num[i])
            ans ++ , mx = max(mx , num[i]);
    printf("%d %d\n" , ans , mx);
    return 0;
}

【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的鄰居 並查集+Treap/STL-set