1. 程式人生 > 其它 >P6441 [COCI2011-2012#6] PASTELE

P6441 [COCI2011-2012#6] PASTELE

題目傳送門

演算法分析:二分+字首和

答案的二分性是比較明顯的,關注 “最大值”“最小值” 就可以看出來。

最外層我們就可以二分答案,而二分的關鍵在於 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;
}

AC

歡迎交流討論,請點個贊哦~