題目要問的東西簡單來說就是,給定平面上的一些點,每個點有它的價值,現在可以用一個斜45度的正方形去蓋他,問蓋到的最大價值。首先一樣可以用座標變換把他變為和坐標軸平行的正方形,只要考慮 ( x , y ) -> ( x-y , x+y ) 即可,所以現在問題變回找的是一個和坐標軸平行的正方形( 並且他的邊長會變為 2*k )。考慮把所有點按照 x 坐標由小到大排序,這樣就可以用雙指標轉換為一維問題了。具體來說,我們對每個 y 坐標都記錄取他的話可以獲得多少的價值,那麼當算到第 i 個點的時候,把他的價值加到他的位置上,並且把所有過期的點( x 坐標和第 i 個點差太多的點 )的價值減掉,然後詢問現在如果使用長度為 2*k 的線段蓋下去,可以蓋到的最大價值為何。而要處理這個問題,只要注意到:每次在一個點加上一個值的時候,會影響到的所有長為 2*k 的區間是連續的,也就是如果我們改成把長 2*k 的區間都看成點,這個點的權重就是這個區間裡的價值總和,那麼每次修改就會變成修改一個區間的權重,查詢就是查詢所有數的最大值,於是就可以用線段樹做了。
code :
#include<bits/stdc++.h> using namespace std; const int maxn=100000+10 , maxc=2000000+10 ; int ST[maxc*4],tag[maxc*4] ; void push(int id) { if(!tag[id]) return ; ST[2*id]+=tag[id] ; tag[2*id]+=tag[id] ; ST[2*id+1]+=tag[id] ; tag[2*id+1]+=tag[id] ; tag[id]=0 ; } void modify(int l,int r,int L,int R,int id,int val) { if(l==L && r==R) { ST[id]+=val ; tag[id]+=val ; return ; } push(id) ; int mid=(L+R)/2 ; if(r<=mid) modify(l,r,L,mid,2*id,val) ; else if(l>mid) modify(l,r,mid+1,R,2*id+1,val) ; else modify(l,mid,L,mid,2*id,val) , modify(mid+1,r,mid+1,R,2*id+1,val) ; ST[id]=max(ST[2*id],ST[2*id+1]) ; } struct P { int x,l,r,val ; bool operator < (const P &rhs) const { return x<rhs.x ; } }p[maxn]; main() { if(fopen("lazy.in","r")) { freopen("lazy.in","r",stdin) ; freopen("lazy.out","w",stdout) ; } int n,k ; scanf("%d%d",&n,&k) ; for(int i=1;i<=n;i++) { int x,y,val ; scanf("%d%d%d",&val,&x,&y) ; p[i]=(P){x-y,max(x+y-2*k,0),x+y,val} ; } sort(p+1,p+n+1) ; int ans=0 ; for(int i=1,j=1;i<=n;i++) { modify(p[i].l,p[i].r,0,maxc,1,p[i].val) ; while(p[j].x+2*k < p[i].x) modify(p[j].l,p[j].r,0,maxc,1,-p[j].val) , j++ ; ans=max(ans,ST[1]) ; } printf("%d\n",ans) ; }
沒有留言:
張貼留言