题意:给n个平面上的炸弹,每个炸弹有一个爆炸范围d,爆炸的时候会引爆d范围内的炸弹(注意距离为曼哈顿距离),这些炸弹可以又炸其他炸弹。然后m次询问,每次引爆编号num的炸弹,输出爆炸的次数,如果已经炸过了就输出0。
因为是平面点,我用了以y升序的mulitset,内部是有序的,就不用再去排序了,而且自带上下二分函数。STL大法好啊。
离散的过程是,x相同的就压入同一个Mset,不同的以x的升序压入Mset。
其实这题就是WA在离散上,果然基本功太渣了ORZ。
以后离散都用sort+unique+lower_bound写了,代码简单,排序复杂度是NlogN,用lower_bound插入是logN,总的复杂度是o(2NlogN)=o(NlogN)。
每次询问的时候先二分出x的左右界,然后枚举二分出y的上下界,然后已经炸了就标记。离散的时候学习了lower_bound和upper_bound,这里写的二分都用这两个函数搞了,代码容易不少。
看代码。
#include<map>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<climits>
#include<list>
#include<iomanip>
#include<stack>
#include<set>
#include<ctime>
#define pb push_back
#define pii pair<int,int>
#define LL long long int
using namespace std;
const int MN=100505;
struct node{ //记录炸弹
int x,y,d;
}bom[MN];
struct st{ //多重集的元素
int y,x;
st(){}
st(int a,int b){y=a,x=b;}
bool operator<(st a)const{
return y<a.y;
}
};
int mak[MN]; //离散
multiset<st>:: iterator L,R;
multiset<st> mst[MN];
bool boom[100015];c //标记是否炸了
int main()
{
int n,m,ca=1;
while( scanf("%d", &n) != EOF && n )
{
printf("Case #%d:\n", ca++);
memset( mak, 0, sizeof(mak) );
memset( boom, 0, sizeof(boom) );
for(int i = 0; i <= n +10; ++i) mst[i].clear();
for(int i = 0; i < n; ++i){
scanf("%d %d %d", &bom[i].x, &bom[i].y, &bom[i].d);
mak[i]=bom[i].x;
}
sort(mak, mak + n);
int cnt = unique(mak, mak + n) - mak;
for(int i=0;i<n;++i)
{
int p=lower_bound(mak, mak + cnt, bom[i].x) - mak; //效率是logN
mst[p].insert( st(bom[i].y, i) );
}
/*sort(bom, bom + n); 按x排序。 这一段是之前的离散,虽然可以AC但是效率不如用STL的高,复杂度多加一个N。
int cnt = 1;
mak[0] = bom[0].x;
mst[0].insert( st(bom[0].y,bom[0].nb) );
for(int i=1;i<n;++i)
{
if(bom[i].x!=mak[cnt-1])
{
mak[cnt]=bom[i].x;
mst[cnt++].insert( st(bom[i].y,bom[i].nb) );
}
else mst[cnt-1].insert( st(bom[i].y,bom[i].nb) );
}
sort(bom, bom + n, cmp); 还原成输入的顺序。 一定要还原成输入的顺序,因为下面询问给的序号是输入时的。
*/
scanf("%d", &m);
while( m-- )
{
int num, ans = 0;
scanf( "%d", &num);
num -= 1; //我们的序号是从0开始离散的
if( boom[ num] ) { puts("0"); continue; }
boom[ num] = 1;
queue< int >q;
q.push(num);
while( !q.empty() )
{
int now = q.front(); q.pop(); ans += 1;
int l = lower_bound( mak, mak + cnt, bom[ now].x - bom[now].d ) - mak; //二分出x的范围
int r = upper_bound( mak, mak + cnt, bom[now].x + bom[now].d ) - mak;
for(int i = l; i < r; ++i)
{
int ef = bom[ now].d - abs( bom[ now].x - mak[i] ); //算出曼哈顿距离下的范围
L = mst[i].lower_bound( st(bom[ now].y - ef, 0) ); //二分出y的范围
R = mst[i].upper_bound( st(bom[ now].y + ef, 0) );
for(multiset<st>:: iterator it = L; it != R; ++it)
{
if( !boom[it -> x])
{
boom[it -> x] = 1; //标记爆炸
q.push(it -> x); //入队看是否又引爆了其他的
}
}
mst[i].erase(L, R); //这一步并不是必要的,因为上面已经标记是不是炸了,而且我不清楚删除的效率如何,但在OJ上有这一步更快。
}
}
printf("%d\n", ans);
}
}
}