题解 P6441 [COCI2011-2012#6] PASTELE

题目传送门

算法分析:二分+前缀和

答案的二分性是比较明显的,关注 “最大值”“最小值” 就可以看出来。因此最外层二分答案。

为了表述方便,记最大的颜色参数为 \(col\)

注意到 \(0\le R_x,G_x,B_x\le256\),范围较小,在没有思路的情况下可以直接 \(col^3\) 枚举三者的最大值,然后再 \(\mathcal{O}(n)\) 判断可行性。总体复杂度为 \(\mathcal{O}(n\times col^3 \times \log_2(col))\)。预计得分 \(50pts\)

那么如何优化?外层二分无法优化,枚举 \(col\) 的范围也只能有常数级别的优化,因此考虑优化掉 \(\mathcal{O}(n)\) 的可行性检查。

当我们知道 \(R_x,G_x,B_x\) 的范围时,是否可以考虑用前缀和 \(\mathcal{O}(1)\) 算出符合范围的蜡笔数?当然可以。考虑使用三维前缀和,用 \(f_{i,j,k}\) 表示满足 \(R_x\le i,G_x\le j,B_x\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\)

code:

#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

欢迎交流讨论,请点个赞哦~

posted @ 2021-06-25 15:41  Maplisky  阅读(113)  评论(0)    收藏  举报