1. 程式人生 > >【Luogu P1502】 視窗的星星

【Luogu P1502】 視窗的星星

→傳送視窗

(複製一下題面好了~)

題目背景

小卡買到了一套新房子,他十分的高興,在房間裡轉來轉去。

題目描述

晚上,小卡從陽臺望出去,“哇~~~~好多星星啊”,但他還沒給其他房間設一個窗戶,天真的小卡總是希望能夠在晚上能看到最多最亮的星星,但是窗子的大小是固定的,邊也必須和地面平行。這時小卡使用了超能力(透視術)知道了牆後面每個星星的位置和亮度,但是小卡發動超能力後就很疲勞,只好拜託你告訴他最多能夠有總和多亮的星星能出現在視窗上。

輸入輸出格式

輸入格式:

本題有多組資料,第一行為T 表示有T組資料T<=10

對於每組資料

第一行3個整數n,W,H,(n<=10000,1<=W,H<=1000000)表示有n顆星星,視窗寬為W,高為H。

接下來n行,每行三個整數xi,yi,li 表示星星的座標在(xi,yi),亮度為li。(0<=xi,yi<2^31)

 

輸出格式:

T個整數,表示每組資料中視窗星星亮度總和的最大值。

輸入輸出樣例

輸入樣例#1: 
2
3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1
輸出樣例#1: 
5
6

說明

小卡買的窗戶框是金屬做的,所以在邊框上的不算在內。


 

喜歡這個題目背景~

這類題目有一個很巧妙的轉化,把移動的視窗(面)和星星(點)轉成 可以覆蓋到星星的(面)和視窗的左下角(點)

什麼意思呢?(可以結合下面的圖片理解)

把每一個星星作為右上角,在它的左下方劃出一片視窗大小的區域,表示只要視窗的左下角落在這一片區域裡就一定能覆蓋到這顆星星。

那麼不同星星的重疊部分就代表能同時覆蓋這幾顆星星了。

(下圖中,只要視窗落在陰影部分,就能同時覆蓋到三顆星星) 

所以這一題的解法就是:

想象一條掃描線從左掃到右邊,只要進入了星星的區域,掃描線上這段區間就可以取到這顆星星的值,等過了區域再減去這顆星星的值。

那就可以用線段樹來做啦,每次挪動找出區間的最值更新答案就可以了。

 

看到題目最後的提示:小卡買的窗戶框是金屬做的,所以在邊框上的不算在內。(驚!

邊框居然不算,好吧那就只好把範圍縮小,到了陰影部分外的那條平行y軸的線就可以把這顆星星的貢獻減掉了。(用Windows XP 畫的的圖,有點醜

還有,星星的座標很大,記得離散化。

具體操作細節可以看程式碼(用了vector來維護座標上加和減的星星)

(程式碼雖然很長,但結構還算清晰吧)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<vector>
  6 #include<algorithm>
  7 
  8 #define For(i,a,b) for(int i=a;i<=b;++i)
  9 #define Pn putchar('\n')
 10 #define llg long long
 11 
 12 using namespace std;
 13 
 14 const int N=2e4+10;
 15 
 16 struct LIS{
 17     int x,y,id;
 18 }Lis[N*2];
 19 
 20 struct Star{
 21     int x1,x2,y1,y2;
 22     llg lgt;
 23     Star(){
 24         x1=0; x2=0; y1=0; y2=0;
 25         lgt=0;
 26     }
 27 }st[N];
 28 
 29 vector<int>ads[N];
 30 vector<int>mns[N]; 
 31 
 32 int tot=0,n,m,W,H,x,y;
 33 llg tag[N*4],mx[N*4],ans=0;
 34 
 35 void read(int &v){       //讀入優化,和輸出優化 
 36     v=0; bool fg=0;
 37     char c=getchar(); if(c=='-')fg=1;
 38     while(c<'0'||c>'9'){c=getchar(); if(c=='-')fg=1;}
 39     while(c>='0'&&c<='9'){v=v*10+c-'0',c=getchar();if(c=='-')fg=1;}
 40     if(fg)v=-v;
 41 }
 42 void read(llg &v){
 43     v=0; bool fg=0;
 44     char c=getchar(); if(c=='-')fg=1;
 45     while(c<'0'||c>'9'){c=getchar(); if(c=='-')fg=1;}
 46     while(c>='0'&&c<='9'){v=v*10+c-'0',c=getchar();if(c=='-')fg=1;}
 47     if(fg)v=-v;
 48 }
 49 void write(int x){
 50     if(x>9)write(x/10);
 51     int xx=x%10;
 52     putchar(xx+'0');
 53 }
 54                         //排序 
 55 bool cmpX(const LIS &a,const LIS &b){
 56     return a.x<b.x;
 57 }
 58 bool cmpY(const LIS &a,const LIS &b){
 59     return a.y<b.y;
 60 }
 61                       //線段樹操作 
 62 void pDown(int o){
 63     llg tg=tag[o];  tag[o]=0;
 64     int ls=o<<1,rs=o<<1|1;
 65     tag[ls]+=tg; tag[rs]+=tg;
 66     mx[ls]+=tg; mx[rs]+=tg;
 67 }
 68 void Ins(int o,int l,int r,int lx,int rx,llg dt){
 69     if(lx<=l&&rx>=r){
 70         mx[o]+=dt; tag[o]+=dt;
 71         return;
 72     }
 73     int m=(l+r)>>1;
 74     int ls=o<<1,rs=o<<1|1;
 75     if(tag[o])pDown(o);
 76     if(lx<=m)Ins(ls,l,m,lx,rx,dt);
 77     if(rx>m)Ins(rs,m+1,r,lx,rx,dt);
 78     mx[o]=max(mx[ls],mx[rs]);
 79 }
 80 
 81 int main(){ 
 82     int T; read(T);
 83     while(T--){ 
 84         tot=0; ans=0;
 85         memset(tag,0,sizeof(tag));
 86         memset(mx,0,sizeof(mx));
 87         
 88         read(n); read(W); read(H);
 89         For(i,1,n){                        //存下星星區域的右上角和左下角 
 90             read(x); read(y); read(st[i].lgt);
 91             st[i].x1=st[i].x2=st[i].y1=st[i].y2=0;
 92             Lis[++tot].x=x;
 93             Lis[tot].y=y,Lis[tot].id=i;
 94              
 95             Lis[++tot].x=x+W-1;
 96             Lis[tot].y=y-H+1,Lis[tot].id=i;
 97         }
 98         Lis[0].x=-2147483600;
 99         Lis[0].y=-2147483600;
100         
101         sort(Lis+1,Lis+tot+1,cmpY);        //分別對X和Y離散化 
102         int ty=0;
103         For(i,1,tot){
104             if(Lis[i].y!=Lis[i-1].y)ty++;
105             int ID=Lis[i].id;
106             if(!st[ID].y2){
107                 st[ID].y2=ty;
108             }else{
109                 st[ID].y1=ty;
110             }
111         }
112         
113         sort(Lis+1,Lis+tot+1,cmpX);
114         int tx=0;
115         For(i,1,tot){
116             if(Lis[i].x!=Lis[i-1].x)tx++;
117             int ID=Lis[i].id; 
118             if(!st[ID].x1){
119                 st[ID].x1=tx;
120             }else{
121                 st[ID].x2=tx;
122             }
123         }
124         
125         For(i,1,tx+1){                  //初始化vector 
126             ads[i].clear();
127             mns[i].clear();
128         }
129         
130         For(i,1,n){
131             int lx,rx;          //把星星掛到相應的橫座標上 
132             lx=st[i].x1;        //ads為加, mns為減 
133             rx=st[i].x2+1; 
134             ads[lx].push_back(i);
135             mns[rx].push_back(i);
136         }
137         For(i,1,tx){
138             int sz;
139             
140             sz=mns[i].size();
141             For(j,0,sz-1){           //先減後加 
142                 int ID=mns[i][j];
143                 int lx,rx;
144                 lx=st[ID].y2;
145                 rx=st[ID].y1; 
146                 Ins(1,1,ty,lx,rx,-st[ID].lgt);
147                 
148             }
149 
150             sz=ads[i].size();
151             For(j,0,sz-1){
152                 int ID=ads[i][j];
153                 int lx,rx;
154                 lx=st[ID].y2;
155                 rx=st[ID].y1;
156                 Ins(1,1,ty,lx,rx,st[ID].lgt);
157             }
158             ans=max(ans,mx[1]);
159         }
160         write(ans); Pn;
161     }
162     return 0;
163 }