Lasers Everywhere 扫描线

题目链接:https://vjudge.net/problem/CodeChef-LAZER

题解:

可以用把询问离线,然后用扫描线的思想来解决这个问题。

把点和询问都丢到一个数组里面,按高度排序。

维护一个“当前高度”,遍历上述数组的过程就相当于让“当前高度”从低往高扫了一遍。

令树状数组中第i个点表示坐标为i的点和坐标为i+1的点间的线条是否包括了当前高度。

每当扫到线条的端点Pi,判断一下Pi左侧的端点Pi-1是否高度比Pi高,是则将Pi-1加到树状数组里,否则准备在下一次当前高度改变的时候把Pi-1从树状数组中删除。

0E36D33C5D878FAA0B90C21832B5ED82.png

当扫到一个询问[l, r]时,在树状数组中查询一下[l, r-1]的和作为答案就行了

代码:

#include <bits/stdc++.h>
#define maxn 100100
using namespace std;

int T, n, q, h[maxn], ans[maxn];
int tr[maxn+5];
queue<int> buf;

void add(int x, int v)
{
    for (int i=x; i<maxn; i+=i&-i)
        tr[i]+=v;
}
int query(int x)
{
    int res=0;
    for (int i=x; i; i-=i&-i)
        res+=tr[i];
    return res;
}

struct item
{
    int f, qh, qx, qy, id;
};
bool cmp(item a, item b)
{
    if (a.qh==b.qh)
        return a.f<b.f;
    return a.qh<b.qh;
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>T;
    while (T--)
    {
        cin>>n>>q;
        vector<item> vec;
        for (int i=1; i<=n; i++)
            cin>>h[i];
        for (int i=1; i<=n; i++)//表示点的f标为1
            vec.push_back((item){1, h[i], i, 0, 0});
        for (int i=1, qh, qx, qy; i<=q; i++)
        {
            cin>>qx>>qy>>qh;//表示询问的f标为2
            vec.push_back((item){2, qh, qx, qy, i});
        }
        sort(vec.begin(), vec.end(), cmp);//按高度,种类排序
        int cot=vec.size(), now=0;
        for (int i=0; i<cot; i++)
        {
            if (vec[i].qh>now)
            {//当前高度改变时要把buf中存的点从树状数组中删掉
                now=vec[i].qh;
                while (!buf.empty())
                {
                    add(buf.front(), -1);
                    buf.pop();
                }
            }
            if (vec[i].f==1)
            {
                //当前点左侧的点高度比它更高,则左侧的点加入树状数组
                if (vec[i].qx>1 && h[vec[i].qx-1]>h[vec[i].qx])
                    add(vec[i].qx-1, 1);
                //当前点左侧点高度比它低,则加入buf中(高度增高后两点间的线段就不再被经过了
                else if (vec[i].qx>1) buf.push(vec[i].qx-1);
                //考虑右侧,基本同上
                if (vec[i].qx<n && h[vec[i].qx+1]>=h[vec[i].qx])
                    add(vec[i].qx, 1);
                else if (vec[i].qx<n) buf.push(vec[i].qx);
            }
            //在树状数组中询问
            if (vec[i].f==2)
                ans[vec[i].id]=query(vec[i].qy-1)-query(vec[i].qx-1);
        }
        while (!buf.empty())
        {//多组数据,所以最后把剩下的点从树状数组里删掉
            add(buf.front(), -1);
            buf.pop();
        }
        for (int i=1; i<=q; i++)
            cout<<ans[i]<<"\n";
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/opppppppp/p/12814720.html