1. 程式人生 > >【BZOJ4548】小奇的糖果

【BZOJ4548】小奇的糖果

→原題傳送門←(by Hzwer)

「題目背景」

小奇不小心讓糖果散落到了地上,它對著滿地的彩色糖果胡思亂想。

「問題描述」

有 N 個彩色糖果在平面上。小奇想在平面上取一條水平的線段,並拾起它上方或下方的所有糖果。求出最多能夠拾起多少糖果,使得獲得的糖果並不包含所有的顏色。

「輸入格式」

包含多組測試資料,第一行輸入一個正整數 T 表示測試資料組數。

接下來 T 組測試資料,對於每組測試資料,第一行輸入兩個正整數 N、K,分別表示點數和顏色數。

接下來 N 行,每行描述一個點,前兩個數 x, y (|x|, |y| ≤ 2^30 – 1) 描述點的位置,最後一個數 z (1 ≤ z ≤ k) 描述點的顏色。

「輸出格式」

對於每組資料在一行內輸出一個非負整數 ans,表示答案。

「樣例輸入」

1

10 3

1 2 3

2 1 1

2 4 2

3 5 3

4 4 2

5 1 2

6 3 1

6 7 1

7 2 3

9 4 2

「樣例輸出」

5

「資料範圍」

對於 30% 的資料,N ≤ 100;

對於 60% 的資料,N ≤ 5000;

對於 100% 的資料,N ≤ 100000,K ≤ 100000,T ≤ 3。


 

 

 

第N道小奇系列的題目了(之前好像也做過一道小奇的糖果來著,不過那是IOI改編,有點難

模擬考的時候寫了暴力居然沒有分(!)

 

下面是自己寫的題解:

  • 這是在糖果是撒在二維平面裡的,所以我們可以通過其中一維的座標離散化,然後通過列舉,轉成單一一維裡的問題。
  • 把橫座標離散化掉,按橫座標從左到右,把橫座標相鄰的糖果用雙向的連結串列串在一起(對於有不同顏色的序列,這樣的操作很常見哦,記下來記下來)。
  • 所以每一次在兩個相同顏色的糖果之間數一下其它糖果的數量,就可以來更新ans了,可以用樹狀陣列來維護區間裡的糖果數量。
  • 然後想象一條掃描線,從下到上掃一遍,每一次把掃描線上的點遮蔽掉,再更新一次答案,這樣就可以完成對於水平線上糖果的更新了。
  • 那水平線下的怎麼更新呢?一樣的嘛,為了減少程式設計複雜度,我們把縱座標取相反數,用一樣的辦法再做一次就可以了。

程式碼~

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<algorithm>
  6 
  7 #define For(i,a,b) for(register int i=a;i<=b;++i)
  8 #define Re register
  9 #define Pn putchar('\n')
 10 using namespace std;
 11 const int N=1e5+10;
 12 int Lc[N],Rc[N],lst[N];
 13 int X[N],sX[N];
 14 struct Point{
 15     int x,y,cl,id;
 16 }pt[N];
 17 int c[N],n,m,x,y,Kx,ans=0;
 18 
 19 inline void read(int &v){
 20     v=0; bool fg=0;
 21     char c=getchar(); if(c=='-')fg=1;
 22     while(c<'0'||c>'9'){c=getchar(); if(c=='-')fg=1;}
 23     while(c>='0'&&c<='9'){v=v*10+c-'0',c=getchar(); if(c=='-')fg=1;}
 24     if(fg)v=-v;
 25 }
 26 void write(int x){
 27     if(x>9)write(x/10);
 28     int xx=x%10;
 29     putchar(xx+'0');
 30 }
 31 bool cmpX(const Point &a,const Point &b){
 32     return a.x<b.x;
 33 }
 34 bool cmpY(const Point &a,const Point &b){
 35     return a.y<b.y;
 36 }
 37 
 38 
 39 int LB(int x){
 40     return x&(-x);
 41 }
 42 void upD(int k,int dt){
 43     for(Re int i=k;i<=n;i+=LB(i)){
 44         c[i]+=dt;
 45     }
 46 }
 47 int Qry(int k){
 48     int ans=0;
 49     for(Re int i=k;i>=1;i-=LB(i)){
 50         ans+=c[i];
 51     }
 52     return ans;
 53 }
 54 
 55 void makeA(int lx,int rx){
 56     if(lx>rx)return;
 57     int tp=Qry(rx)-Qry(lx-1);
 58     ans=max(tp,ans);
 59 }
 60 void PickupCandy(){
 61     
 62     X[0]=0; X[n+1]=n+1;
 63     memset(lst,0,sizeof(lst));
 64     memset(c,0,sizeof(c));
 65     
 66     sort(pt+1,pt+n+1,cmpX);
 67     
 68     For(i,1,n){
 69         upD(pt[i].x,1);
 70     }
 71         
 72     For(i,1,n){
 73         int ID=pt[i].id;
 74         int prC=lst[pt[i].cl];
 75         Lc[ID]=prC; Rc[ID]=n+1;
 76         if(prC) Rc[prC]=ID;
 77         makeA(X[prC]+1,X[ID]-1);
 78         lst[pt[i].cl]=ID;
 79     }
 80     For(i,1,Kx){
 81         makeA(X[lst[i]]+1,X[n+1]);
 82     }
 83 
 84     sort(pt+1,pt+n+1,cmpY);
 85     
 86     int Px=1;
 87     For(i,1,n){
 88         while(Px<=n&&pt[i].y==pt[Px].y){
 89             upD(X[pt[Px].id],-1);
 90             Px++;
 91         }
 92         int ID=pt[i].id;
 93         Lc[Rc[ID]]=Lc[ID]; Rc[Lc[ID]]=Rc[ID];
 94         makeA(X[Lc[ID]]+1,X[Rc[ID]]-1);
 95     }
 96     return;
 97 }
 98 
 99 int main(){
100     //freopen("candy.in","r",stdin);
101     //freopen("candy.out","w",stdout);
102     int T; read(T);
103     while(T--){
104         read(n); read(Kx);
105         ans=0;
106         For(i,1,n){
107             read(pt[i].x); read(pt[i].y); read(pt[i].cl);
108             pt[i].id=i;
109             sX[i]=pt[i].x;
110         }
111         sort(sX+1,sX+n+1);
112         For(i,1,n){
113             X[i]=lower_bound(sX+1,sX+n+1,pt[i].x)-sX;
114             pt[i].x=X[i];
115         }
116         PickupCandy();
117         For(i,1,n)pt[i].y*=-1;
118         PickupCandy();
119         write(ans); Pn;
120     }
121 //    fclose(stdin); fclose(stdout);
122     return 0;
123 }