HDU 6681(樹狀數組統計平面內射線的交點個數)

HDU 6681(樹狀數組,統計平面內射線的交點個數)

題目鏈接:傳送門

題意:給出k條射線,求射線將nmn*m 的區域分成幾個聯通塊。每兩條射線的端點x座標和y座標都互不相同。

思路:根據 歐拉公式 可以推導出聯通塊的個數等於射線的焦點個數c+1。但其實賽場上根本不知道這個定理,但有個很明顯的道理,對於每條豎線,每條橫着的射線與該豎線相交都會使聯通塊個數+1.(注意因爲題目限制,這個射線的端點不會在邊界上,不然在邊界的射線會使聯通塊+2)。所以我們直接統計出射線的交點個數即可,這是個經典問題(但是我當時不會

​ 其實對於每條豎着的射線(假設端點爲x0,y0x0,y0​),只要能統計出它左邊橫着的射線與之相交的射線個數和,即相交的射線的端點x,yx,y​滿足射線方向向右且x<x0x<x0​,y在射線(x0,y0x0,y0​)縱座標覆蓋範圍之內即可。統計左邊橫着的射線與之相交的射線個數也是同樣道理

​ 因爲射線的個數kk的範圍爲1e51e5​,故使用二維前綴和肯定是不行的。

​ 我們可以分類統計,我們先來統計每條射線左邊橫着的射線與之相交的射線個數,我們可以先將所有豎直方向的射線按xx從小到大排序,所有橫着的射線按xx從小到大排序,對於第ii 條豎直方向的射線,我們可以處理所有左邊的橫着向右的射線,然後把他們的yy座標加入樹狀數組 (這裏需要對yy座標進行離散化)。然後可以O(logn)O(logn)統計出樹狀數組內yy在第一條射線縱座標覆蓋範圍之內的點的個數。雙指針優化後可達到O(n)統計的效果。

歐拉公式:歐拉公式介紹

代碼:

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
const int L=0,R=1,U=2,D=3;
ll X[N],Y[N],kinds[N],n,m,K,pes[N];
ll bt[N];
ll maxm;
struct node
{
    ll x,y,kind;
    node() {}
    node(ll x,ll y,ll kind):x(x),y(y),kind(kind) {}


    bool operator <(const node& other) const
    {
        return x<other.x;
    }
};
vector<node> row,col;
void disperse(ll X[],ll n)
{
    ll cnt=0;
    for(ll i=1; i<=n; ++i)
        pes[cnt++]=X[i];
    sort(pes,pes+cnt);
    cnt=unique(pes,pes+cnt)-pes;
    for(ll i=1; i<=n; ++i)
    {
        X[i]=lower_bound(pes,pes+cnt,X[i])-pes+1;
    }
}
ll lowbit(ll k)
{
    return k&-k;
}
void add(ll k,ll val)
{
    while(k<=maxm)
    {
        bt[k]+=val;
        k+=lowbit(k);
    }
}
ll get(ll k)
{
    ll ans=0;
    while(k)
    {
        ans+=bt[k];
        k-=lowbit(k);
    }
    return ans;
}
ll calc(ll l,ll r)
{
    return get(r)-get(l-1);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>K;
        for(ll i=1; i<=K; ++i)
        {
            char c;
            cin>>X[i]>>Y[i]>>c;
            switch (c)
            {
            case 'L':
                kinds[i]=L;
                break;
            case 'R':
                kinds[i]=R;
                break;
            case 'U':
                kinds[i]=U;
                break;
            case 'D':
                kinds[i]=D;
                break;
            }
        }
        Y[K+1]=m;
        //離散化
        disperse(Y,K+1);
        maxm=Y[K+1];
        row.clear(),col.clear();
        //加入行列數組
        for(ll i=1; i<=K; ++i)
        {
            if(kinds[i]==L||kinds[i]==R)
            {
                row.push_back({X[i],Y[i],kinds[i]});
            }
            else
            {
                col.push_back({X[i],Y[i],kinds[i]});
            }
        }
        sort(row.begin(),row.end());
        sort(col.begin(),col.end());
        mset(bt,0);
        int p=0;
        ll ans=0;
        //進行雙指針並樹狀數組維護信息
        /*統計每條豎線與左側射線的交點個數*/
        for(ll i=0ll; i<col.size(); ++i)
        {
            ll x=col[i].x,y=col[i].y;
            while(p<row.size()&&row[p].x<x)
            {
                if(row[p].kind==R)
                    add(row[p].y,1);
                ++p;
            }
            if(col[i].kind==U)
                ans+=calc(y,maxm);
            else
                ans+=calc(1,y);
        }
        /*統計每條豎線與右側射線的交點個數*/
        mset(bt,0);
        p=row.size()-1;
        for(ll i=col.size()-1ll; ~i; --i)
        {

            ll x=col[i].x,y=col[i].y;
            while(p>=0&&row[p].x>x)
            {
                if(row[p].kind==L)
                    add(row[p].y,1);
                --p;
            }

            if(col[i].kind==U)
                ans+=calc(y,maxm);
            else
                ans+=calc(1,y);
        }
        cout<<ans+1<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章