ZOJ 3521 Fairy Wars【并查集,扫描线,set维护】
题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3521以前一直以为扫描线这么神奇的想法应该有很复杂的代码,今天亲身写了一下,发现只要数据结构维护的好,代码真的很短。
感谢wolf5x大神、schnee大神对我这蒟蒻的指导。
题目大意:有一个圆形炸弹,给定二维坐标和半径;平面上N个点(N<=50000),每个点的二维坐标给出;给定一个L,是这些点的影响范围,影响范围是以这个点为中心的长度为L的正方形;影响范围内的点可以互相影响,连锁具有传递性。问,把炸弹引爆的话,会有多少个点被连锁。
做法:扫描线+set维护+并查集+枚举(最后一个随便说说。。。)
具体:裸的想法就是连边并查集,这个是O(N^2)的复杂度,铁挂;几何题的自然想法就是想到极角排序和扫描线以及线段树上面去。
极角排序没有好想法,线段树不会搞,就想扫描线+set维护的神想法。
一开始想环形扫描线想不出个所以然来,经大神们点拨后开始拿传统的坐标序扫描线搞之。
将这N个点按x坐标排序,维护两根扫描线或者说一个队列,一开始队列头尾都指向最左点,队列尾向右移动的时候先判断头尾两点的横坐标差值是否大于L,大于则把对头右移直到满足条件,同时将弹队的这些点在set中删除:
set中的元素是一个pair,first是点的纵坐标,second是点的id,这样就保证了内部没有重复元素,内部序按照y排序;然后将队尾的元素插入set中,因为插入函数的返回值是一个pair,pair的first即为队尾的点在set中的插入位置,second是插入成功与否的bool。
所以it-1(it != set.begin() )和it+1(it+1 != set.end() )分别是set中与当前插入点y坐标最相近的两个点,比较当前点与这两个的y坐标之差是否小于等于L,是则union之。可以归纳证明,需要判断的只有最近的两个点,因为更远的点已经在之前判断过了。
这样扫描一遍过后,就剩下几个联通块;再扫描一遍,判断圆是否与某个联通块相交,若相交则将联通块大小加到答案上,最后输出答案即可。
注意点:
1.为了防止浮点数误差,判断点在圆内的时候,直接用整数平方判断的,所以会超int,注意long long。
2.维护的时候注意尽量少写特判语句,比如初始点什么的,用一个统一化的判断语句来判断弹队与否,比如front 与 tail的距离什么的。。
3.set的insert函数的返回值的first是个指针,所以当我们插入的元素即为一个pair时,访问pair的first需要用->而非.
4.set的元素是一一对应的,无重复元素;用insert来实现find的功能,原因在于set没有单关键字查询功能,或者说没有我们想要的对于pair内某一元素的查询功能,所以用insert来实现find,是一个更巧妙的想法。
5.我是个蒟蒻,又tmd把并查集写错了。
6.思维比较神的题、各种算法联合在一起的题目不一定需要很大的代码量,不要被吓住。
附上代码:
#include
<
iostream
>
#include < cstdio >
#include < cstring >
#include < set >
#include < queue >
#include < algorithm >
#include < cstdlib >
#include < vector >
#define maxn 50005
using namespace std;
struct point
{
long long x,y;
point(){}
point( long long _x, long long _y):x(_x),y(_y){}
point operator - ( const point p)
{
return point(x - p.x,y - p.y);
}
long long norm2()
{
return x * x + y * y;
}
}p[maxn],c;
typedef set < pair < long long , int > > SET;
SET S;
SET::iterator it;
long long ab( long long x)
{
return x >= 0 ? x : - x;
}
bool cmp( const point & a, const point & b) // for sort
{
return a.x < b.x;
}
int n,l;
long long r;
int f[maxn],ra[maxn];
bool has[maxn];
int find( int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
void un( int a, int b)
{
a = find(a);
b = find(b);
if (a != b)
{
if (ra[a] > ra[b])
{
f[b] = a;
ra[a] += ra[b];
}
else
{
f[a] = b;
ra[b] += ra[a];
}
}
}
void gao()
{
S.clear();
int front = 1 ;
int a,b;
for ( int i = 1 ;i <= n;i ++ )
{
while (ab(p[i].x - p[front].x) > l)
{
S.erase(make_pair(p[front].y,front));
front ++ ;
}
it = S.insert(make_pair(p[i].y,i)).first;
if (it != S.begin())
{
it -- ;
if (ab(p[i].y - (it -> first)) <= l)
{
a = i;
b = it -> second;
un(a,b);
}
it ++ ;
}
it ++ ;
if (it != S.end() && ab(p[i].y - (it -> first)) <= l)
{
a = i;
b = it -> second;
un(a,b);
}
}
}
int main()
{
while (scanf( " %d %lld %d " , & n, & r, & l) == 3 )
{
fill(ra,ra + n + 1 , 1 );
l /= 2 ;
for ( int i = 1 ;i <= n;i ++ )
f[i] = i;
for ( int i = 1 ;i <= n;i ++ )
scanf( " %lld %lld " , & p[i].x, & p[i].y);
sort(p + 1 ,p + n + 1 ,cmp);
scanf( " %lld %lld " , & c.x, & c.y);
gao();
int ans = 0 ;
memset(has, 0 , sizeof (has));
for ( int i = 1 ;i <= n;i ++ )
{
if ((p[i] - c).norm2() <= r * r)
{
int fuck = find(i);
if ( ! has[fuck])
{
ans += ra[fuck];
has[fuck] = true ;
}
}
}
printf( " %d\n " ,ans);
}
}
#include < cstdio >
#include < cstring >
#include < set >
#include < queue >
#include < algorithm >
#include < cstdlib >
#include < vector >
#define maxn 50005
using namespace std;
struct point
{
long long x,y;
point(){}
point( long long _x, long long _y):x(_x),y(_y){}
point operator - ( const point p)
{
return point(x - p.x,y - p.y);
}
long long norm2()
{
return x * x + y * y;
}
}p[maxn],c;
typedef set < pair < long long , int > > SET;
SET S;
SET::iterator it;
long long ab( long long x)
{
return x >= 0 ? x : - x;
}
bool cmp( const point & a, const point & b) // for sort
{
return a.x < b.x;
}
int n,l;
long long r;
int f[maxn],ra[maxn];
bool has[maxn];
int find( int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
void un( int a, int b)
{
a = find(a);
b = find(b);
if (a != b)
{
if (ra[a] > ra[b])
{
f[b] = a;
ra[a] += ra[b];
}
else
{
f[a] = b;
ra[b] += ra[a];
}
}
}
void gao()
{
S.clear();
int front = 1 ;
int a,b;
for ( int i = 1 ;i <= n;i ++ )
{
while (ab(p[i].x - p[front].x) > l)
{
S.erase(make_pair(p[front].y,front));
front ++ ;
}
it = S.insert(make_pair(p[i].y,i)).first;
if (it != S.begin())
{
it -- ;
if (ab(p[i].y - (it -> first)) <= l)
{
a = i;
b = it -> second;
un(a,b);
}
it ++ ;
}
it ++ ;
if (it != S.end() && ab(p[i].y - (it -> first)) <= l)
{
a = i;
b = it -> second;
un(a,b);
}
}
}
int main()
{
while (scanf( " %d %lld %d " , & n, & r, & l) == 3 )
{
fill(ra,ra + n + 1 , 1 );
l /= 2 ;
for ( int i = 1 ;i <= n;i ++ )
f[i] = i;
for ( int i = 1 ;i <= n;i ++ )
scanf( " %lld %lld " , & p[i].x, & p[i].y);
sort(p + 1 ,p + n + 1 ,cmp);
scanf( " %lld %lld " , & c.x, & c.y);
gao();
int ans = 0 ;
memset(has, 0 , sizeof (has));
for ( int i = 1 ;i <= n;i ++ )
{
if ((p[i] - c).norm2() <= r * r)
{
int fuck = find(i);
if ( ! has[fuck])
{
ans += ra[fuck];
has[fuck] = true ;
}
}
}
printf( " %d\n " ,ans);
}
}
Backspace