《演算法競賽進階指南》0x43線段樹 掃描線演算法 POJ2482
阿新 • • 發佈:2020-07-15
題目連結:http://poj.org/problem?id=2482
給出每個點框定的區域,求區域疊加的最大值,可以通過如下演算法:
將每個可行點都標記,記錄這些點上的權值,維護一個葉結點是一個權值點的線段樹,更新的時候注意,由於所有的點都是可行點,所以右邊界要在最後刪除,遇到同樣的x座標的,優先疊加左邊界的權值,然後去除結束矩形的權值。
程式碼:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn = 10010; struct node{int l,r; int ans,add; }t[maxn<<3]; struct P{ unsigned int x,y1,y2; int c; bool operator < (const P& a)const { return x<a.x || (x==a.x && c>a.c); } }a[maxn<<1]; unsigned int b[maxn<<1]; int n; int num; unsigned int w,h; void build(intrt,int l,int r){ t[rt].l=l; t[rt].r=r; t[rt].add=0; t[rt].ans=0; if(l==r)return ; int mid=l+r>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } void pushdown(int rt){ if(t[rt].add){ t[rt<<1].add+=t[rt].add; t[rt<<1|1].add+=t[rt].add; t[rt<<1].ans+=t[rt].add; t[rt<<1|1].ans+=t[rt].add; t[rt].add=0; } return ; } void update(int rt,int L,int R,int C){ if(L<=t[rt].l && t[rt].r<=R){ t[rt].ans+=C; t[rt].add+=C; return ; } pushdown(rt); int mid=(t[rt].l+t[rt].r)>>1; if(L<=mid)update(rt<<1,L,R,C); if(R>mid)update(rt<<1|1,L,R,C); t[rt].ans=max(t[rt<<1].ans,t[rt<<1|1].ans); } int get(unsigned int x){ return lower_bound(b+1,b+num+1,x)-b; } void solve(){ unsigned int x,y; int c; for(int i=1;i<=n;i++){ int k=i<<1; scanf("%u%u%d",&x,&y,&c); a[k-1].x=x,a[k-1].y1=y,a[k-1].y2=y+h-1,a[k-1].c=c; a[k].x=x+w-1,a[k].y1=y,a[k].y2=y+h-1,a[k].c=-c; b[k-1]=y; b[k]=y+h-1; } sort(b+1,b+2*n+1); num=unique(b+1,b+2*n+1)-(b+1); sort(a+1,a+2*n+1); build(1,1,num); int ans=0; for(int i=1;i<=2*n;i++){ int y1=get(a[i].y1); int y2=get(a[i].y2); update(1,y1,y2,a[i].c); ans=max(ans,t[1].ans);//t[1]中儲存了掃描段中的最大值 } cout<<ans<<endl; } int main(){ while(cin>>n>>w>>h)solve(); return 0; }