[离散+二分]HDU4400 Mines

来源:互联网 发布:mac pdf reader pro 编辑:程序博客网 时间:2024/04/27 23:09

题意:给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 intusing 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);        }    }}


0 0
原创粉丝点击