HDU 6681(樹狀數組,統計平面內射線的交點個數)
題目鏈接:傳送門
題意:給出k條射線,求射線將 的區域分成幾個聯通塊。每兩條射線的端點x座標和y座標都互不相同。
思路:根據 歐拉公式 可以推導出聯通塊的個數等於射線的焦點個數c+1。但其實賽場上根本不知道這個定理,但有個很明顯的道理,對於每條豎線,每條橫着的射線與該豎線相交都會使聯通塊個數+1.(注意因爲題目限制,這個射線的端點不會在邊界上,不然在邊界的射線會使聯通塊+2)。所以我們直接統計出射線的交點個數即可,這是個經典問題(但是我當時不會
其實對於每條豎着的射線(假設端點爲),只要能統計出它左邊橫着的射線與之相交的射線個數和,即相交的射線的端點滿足射線方向向右且,y在射線()縱座標覆蓋範圍之內即可。統計左邊橫着的射線與之相交的射線個數也是同樣道理
因爲射線的個數的範圍爲,故使用二維前綴和肯定是不行的。
我們可以分類統計,我們先來統計每條射線左邊橫着的射線與之相交的射線個數,我們可以先將所有豎直方向的射線按從小到大排序,所有橫着的射線按從小到大排序,對於第 條豎直方向的射線,我們可以處理所有左邊的橫着向右的射線,然後把他們的座標加入樹狀數組 (這裏需要對座標進行離散化)。然後可以統計出樹狀數組內在第一條射線縱座標覆蓋範圍之內的點的個數。雙指針優化後可達到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;
}