P6441 [COCI2011-2012#6] PASTELE
阿新 • • 發佈:2021-06-25
演算法分析:二分+字首和
答案的二分性是比較明顯的,關注 “最大值”“最小值” 就可以看出來。
最外層我們就可以二分答案,而二分的關鍵在於 check 函式。
為了表述方便,記顏色數為 \(col\)。
注意到 \(0\le R_i,G_i,B_i\le256\),範圍較小,在沒有思路的情況下可以直接 \(256^3\) 列舉三者的最大值,然後再 \(\mathcal{O}(n)\) 判斷每支蠟筆是否符合條件。總體複雜度為 \(\mathcal{O}(n\times col^3 \times log_2(col))\)。預計得分\(50pts\)。
那麼如何優化?
二分無法優化,列舉範圍也只能有常數級別的優化,因此考慮優化掉 \(\mathcal{O}(n)\)
當我們知道三個引數的範圍時,是否可以考慮用字首和 \(\mathcal{O}(1)\) 算出符合範圍的蠟筆數?當然可以。考慮使用三維字首和,用 \(f_{i,j,k}\) 表示滿足 \(R_i\le i,G_i\le j,B_i\le k\) 的蠟筆總數。我們類比二維字首和,利用容斥原理計算三維字首和,遵循 “奇加偶減” 原則,即:
\[f_{i,j,k}=f_{i,j,k}+f_{i-1,j,k}+f_{i,j-1,k}+f_{i,j,k-1}-f_{i-1,j-1,k}-f_{i-1,j,k-1}-f_{i,j-1,k-1}+f_{i-1,j-1,k-1} \]於是可以 \(\mathcal{O}(1)\)
總時間複雜度 \(\mathcal{O}(col^3 \times log_2(col))\)。
注意:顏色存在0,輸入時因+1,便於字首和處理。輸出時記得-1。
程式碼如下:
#include<bits/stdc++.h> #define reg register #define F(i,a,b) for(reg int i=a;i<=b;++i) using namespace std; inline int read(); const int N=1e5+1,M=260; int n,k,ans=1e9; int f[M][M][M]; struct P { int r,g,b; inline void inp() { r=read()+1,g=read()+1,b=read()+1; //由於顏色存在 0 字首和不易處理,因此+1 } } a[N],Mx,Mi,rec; inline P max(P a,P b) { return (P) { max(a.r,b.r),max(a.g,b.g),max(a.b,b.b) }; } inline P min(P a,P b) { return (P) { min(a.r,b.r),min(a.g,b.g),min(a.b,b.b) }; } inline void init() { F(i,1,n)++f[a[i].r][a[i].g][a[i].b]; F(i,1,257) { F(j,1,257) { F(k,1,257) { f[i][j][k]+=f[i-1][j][k]+f[i][j-1][k]+f[i][j][k-1] -f[i-1][j-1][k]-f[i-1][j][k-1]-f[i][j-1][k-1] +f[i-1][j-1][k-1]; } } }//統計三維字首和 } inline bool check(int col) { //一點列舉邊界小優化 F(r,Mi.r,Mx.r) { F(g,Mi.g,Mx.g) { F(b,Mi.b,Mx.b) { reg int lr=max(1,r-col),lg=max(1,g-col),lb=max(1,b-col); reg int s=f[r][g][b]-f[lr-1][g][b]-f[r][lg-1][b]-f[r][g][lb-1] +f[lr-1][lg-1][b]+f[lr-1][g][lb-1]+f[r][lg-1][lb-1] -f[lr-1][lg-1][lb-1]; //容斥原理計算符合條件的數量。 if(s>=k){ rec={r,g,b}; return true; } } } } return false; } inline void output(){//輸出方案 printf("%d\n",ans); reg int lr=rec.r-ans,lg=rec.g-ans,lb=rec.b-ans; F(i,1,n){ if(a[i].r>=lr and a[i].r<=rec.r){ if(a[i].g>=lg and a[i].g<=rec.g){ if(a[i].b>=lb and a[i].b<=rec.b){ //記得-1 printf("%d %d %d\n",a[i].r-1,a[i].g-1,a[i].b-1); --k; if(!k)return; } } } } } int main() { n=read(),k=read(); F(i,1,n)a[i].inp(),Mx=max(Mx,a[i]),Mi=min(Mi,a[i]); init(); reg int ll=0,rr=max(Mx.b-Mi.b,max(Mx.g-Mi.g,Mx.r-Mi.r)),mid; while(ll<=rr) { mid=ll+rr>>1; check(mid)?rr=mid-1,ans=mid:ll=mid+1; } output(); return 0; } inline int read() { reg int x=0; reg char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; }
歡迎交流討論,請點個贊哦~