思路
set+並查集+玄學亂搞……
一道對STL的靈活運用題……
這個我也是看了Claris大神的部落格才寫出來的(%%%)
好了廢話不多說,我們進入正題。
這道題其實就是求一個點為右上方能圍住多少點(廢話)
按照官方題解的思路,首先要用一個玄學掃描線維護。
既然要掃描線,那麼肯定首先排序啊。
按y軸從大到小排序,掃描從上往下。
好了現在我們拋開被圍的點,只注意柵欄的頂點。
首先,一個柵欄的頂點向左和下發出射線。
向下發出的射線在x軸上就是它本身的位置,只要在set中保留這個點就相當於儲存了這條射線。
那麼我們現在只考慮向左發出的射線。
若有下面兩個點:
這裡如果A加入的時間比D早,那麼最終形成的就是這樣:
如果A比D晚,則是這樣:
可以看出,在上面的第二種情況下,A點要在D加入時從set中刪去。(見代碼中的“注釋1”)
好了,這一種情況我們就解決了。
但是範例中3號點和4號點的情況呢。
可以看出,範例中是3號點包含4號點。
統計時,3號點的答案要加上4號點的答案。
但是,如果是1和3的情況,雖然3也包含1,但是並不需要把3號點加上1號點的答案。(“注釋5”)
那麼我們可以建立一個並查集。
在每次加入一個點的時候,記錄一個在它右側且比它的時間大的點;(見代碼中的“注釋2”)
最後倒序枚舉每一個點,用並查集維護,這個點的答案就是這個點所屬並查集的數量之和。(“注釋3”)
然後將這個點與插入時在他右側的點,加入一個並查集中。
最後一個問題:上面斜體字有啥用。
“倒序枚舉”,是由於在上面統計時我們已經把所有的點加入到整個圖中。
而現在需要分開統計,倒序刪去每一條邊,就相當於一個拓撲排序。(“注釋4”) 代碼
//STL有啥不好啦,代碼量短,如果手寫splay還要各種操作,STL多簡潔明了啊#include <cstdio>#include <set>#include <utility>#include <algorithm>const int maxn=300000;struct point{ int x,y,t;};typedef std::set<std::pair<int,int> > set_pair;typedef std::set<std::pair<int,int> >::iterator set_pair_iterator;set_pair s;int cnt[maxn+10],n,m,par[maxn+10],ans[maxn+10];point p[(maxn<<1)+10];struct union_find_set{ int fa[maxn+10]; int find(int x) { return fa[x]?fa[x]=find(fa[x]):x; } inline int merge(int x,int y) { x=find(x); y=find(y); if(x!=y) { fa[x]=y; cnt[y]+=cnt[x]; } return 0; }};union_find_set f;bool cmp(const point &a,const point &b){ if(a.y==b.y) { return a.x<b.x; } return a.y>b.y;}int main(){ scanf("%d",&n); for(register int i=1; i<=n; ++i) { scanf("%d%d",&p[i].x,&p[i].y); p[i].x=(p[i].x<<1)-1; p[i].y=(p[i].y<<1)-1; p[i].t=0; } scanf("%d",&m); for(register int i=1; i<=m; ++i) { scanf("%d%d",&p[n+i].x,&p[n+i].y); p[n+i].x<<=1; p[n+i].y<<=1; p[n+i].t=i; } std::sort(p+1,p+n+m+1,cmp); for(register int i=1; i<=n+m; ++i) { std::pair<int,int> q=std::make_pair(p[i].x,p[i].t); if(p[i].t) { s.insert(q); set_pair_iterator now=s.find(q); if((++now)!=s.end()) { par[p[i].t]=now->second;//注釋2:記錄在它右側的點 } while(1)//注釋1:這個while迴圈就是在進行這個步驟 { now=s.find(q); if(now==s.begin()) { break; } if((--now)->second<p[i].t) { break; } s.erase(now); } } else { set_pair_iterator it=s.lower_bound(q); if(it!=s.end()) { ++cnt[it->second]; } } } for(register int i=m; i; --i)//注釋3:這個for迴圈就是在進行這樣的步驟 //注釋4:注意倒序枚舉 { ans[i]=cnt[f.find(i)]; if(par[i]) { f.merge(i,par[i]); //注釋5:這裡並沒有特殊處理出上面的情況 //因為如果par的時間大於i的時間,那麼加入同一併查集了並不影響結果 } } for(register int i=1; i<=m; ++i) { printf("%d\n",ans[i]); } return 0;}