NEERC2017 Archery Tournament 线段树 新套路
題目鏈接
http://codeforces.com/gym/101630/attachments/download/6401/20172018-acmicpc-northeastern-european-regional-contest-neerc-17-en.pdf
題意
給出一些圓,這些圓圓心在x軸上方,且與x軸相切,任意時刻,不存在圖上的圓相交,給出兩種操作:增加一個圓;向圖中發(fā)送一顆子彈,如果擊中某個圓,輸出該圓編號并且刪掉這個圓。如果未擊中,則輸出-1。
題解
圓與x軸相切,且圓之間不相交,這說明,如果先把圓抽象成平行于x軸的線段的話,那么包含子彈橫坐標的線段不超過log(N)log(N)個,這是一個很關鍵的條件。
如果我們能找出這些線段,那么我們就可以進行包含判定了。
下面關鍵的問題在于如何找出這些線段。
這里有一個騷操作,那就是使用線段樹的方式。
我們先把圓一切兩半,分別考慮,先考慮子彈打在右半邊的情況,左半邊的情況類似。
對于圓的右半邊表示成線段就是[xr,xr+r][xr,xr+r],因為圓心不會重合,所以所有的xrxr都不相同,這也正是我們要把圓切開的原因。
將線段存在線段樹里,線段樹xrxr位置存放xr+rxr+r,代表一個線段。并且要求線段樹支持區(qū)間查詢最大值的操作。
下面展示如何定位到所有的包含子彈橫坐標xbxb的線段。
我們采用分治的方法,初始待考慮區(qū)間為[0,xb][0,xb](這樣保證了線段的左端點<xb<xb<script type="math/tex" id="MathJax-Element-917">xbxb(相當于判斷線段的右端點>xb>xb),如果大于,說明在這個區(qū)間內有滿足的線段,將區(qū)間進一步細分。
直到區(qū)間細分到長度為11<script type="math/tex" id="MathJax-Element-920">1</script>,停止,這就是一個滿足條件的線段。
代碼
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <map> using namespace std; const int maxn = 5e5+7; int n; int tp,x,y,len; int segmx[maxn<<2],segmi[maxn<<2]; int al[maxn],cat = 0; int mp[maxn]; #define pr(x) cout<<#x<<":"<<x<<endl inline int getid(int x){return lower_bound(al,al+len,x) - al; } void updatemx(int rt,int l,int r,int pos,int x){if(l == r){segmx[rt] = x;return ;}int mid = (l+r)/2;if(pos <= mid) updatemx(rt*2,l,mid,pos,x);else updatemx(rt*2+1,mid+1,r,pos,x);segmx[rt] = max(segmx[rt*2],segmx[rt*2+1]); } void updatemi(int rt,int l,int r,int pos,int x){if(l == r){segmi[rt] = x;return ;}int mid = (l+r)/2;if(pos <= mid) updatemi(rt*2,l,mid,pos,x);else updatemi(rt*2+1,mid+1,r,pos,x);segmi[rt] = min(segmi[rt*2],segmi[rt*2+1]); }int querymx(int rt,int l,int r,int ul,int ur){if(ul <= l && r <= ur)return segmx[rt];if(r < ul || l > ur) return -2e9;int mid = (l+r)/2;int ansl = querymx(rt*2,l,mid,ul,ur);int ansr = querymx(rt*2+1,mid+1,r,ul,ur);return max(ansl,ansr); }int querymi(int rt,int l,int r,int ul,int ur){if(ul <= l && r <= ur)return segmi[rt];if(r < ul || l > ur) return 2e9;int mid = (l+r)/2;int ansl = querymi(rt*2,l,mid,ul,ur);int ansr = querymi(rt*2+1,mid+1,r,ul,ur);return min(ansl,ansr); } int tps[maxn],xs[maxn],ys[maxn]; int ans[maxn]; bool check(long long x,long long y,int id){long long dx = x - xs[id];long long dy = y - ys[id];if(dx*dx + dy*dy < y*y) return 1;return 0; } void dcmx(int l,int r,int id){int y = querymx(1,0,len-1,l,r);if(y <= xs[id]) return ;if(l == r){int x = al[l];y = y - x;if(check(x,y,id)) {ans[id] = mp[l];mp[l] = 0;updatemx(1,0,len-1,l,-2e9);updatemi(1,0,len-1,l,2e9);}return ;}int mid = (l+r)/2;dcmx(l,mid,id);dcmx(mid+1,r,id);} void dcmi(int l,int r,int id){int y = querymi(1,0,len-1,l,r);if(y >= xs[id]) return ;if(l == r){int x = al[l];y = -y + x;if(check(x,y,id)) {ans[id] = mp[l];mp[l] = 0;updatemi(1,0,len-1,l,2e9);updatemx(1,0,len-1,l,-2e9);}return ;}int mid = (l+r)/2;dcmi(l,mid,id);dcmi(mid+1,r,id);} int main(){scanf("%d",&n);for(int i = 1;i <= n;++i){scanf("%d%d%d",&tp,&x,&y);tps[i] = tp,xs[i] = x,ys[i] = y;if(tp == 1){al[cat++] = x;al[cat++] = x-y;al[cat++] = x+y;}else{al[cat++] = x;}}sort(al,al+cat);len = unique(al,al+cat) - al;memset(mp,0,sizeof(mp));for(int i = 0;i < maxn<<2;++i) segmx[i] = -2e9,segmi[i] = 2e9;for(int i = 1;i <= n;++i){int idx = getid(xs[i]);if(tps[i] == 1){mp[idx] = i;updatemx(1,0,len-1,getid(xs[i]),xs[i]+ys[i]);updatemi(1,0,len-1,getid(xs[i]),xs[i]-ys[i]);}else{dcmx(0,idx,i);dcmi(idx,len-1,i);}}for(int i = 1;i <= n;++i){if(tps[i] == 2){printf("%d\n",ans[i]?ans[i]:-1);}}return 0; }總結
以上是生活随笔為你收集整理的NEERC2017 Archery Tournament 线段树 新套路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三国志11怎么玩 三国志11新手攻略
- 下一篇: 普通话测试话题范文10篇 推荐10篇测试