【COCI2012】蜡笔

题目描述

ABC生日收到N支蜡笔,每支蜡笔的颜色是三原色红绿蓝的组合,第i个蜡笔的颜色用Ri表示红色,Gi表示绿色,Bi表示蓝色。

蜡笔i和蜡笔j的颜色差异定义为max(|Ri-Rj|,|Gi-Gj|,|Bi-Bj|),多支蜡笔的颜色差异定义为其中任意两个蜡笔颜色的最大差异值。

ABC想从N支蜡笔中选出K支出来,要求这K支蜡笔的颜色差异值最小。

50%的数据满足0<=Ri,Gi,Bi<=20;
80%的数据满足0<=Ri,Gi,Bi<=50;
100%的数据满足0<=Ri,Gi,Bi<=255,N<=100000

50分算法

s[R][G][B] 记录颜色值为 (R,G,B) 的蜡笔总数,然后枚举上界,统计答案即可。

80分算法

想到50分,就很容易想到80分的算法了。用一个前缀和就可以省掉一个枚举,轻松拿80分。

满分算法

当我们确定一个答案 ans ,和三个状态的上界,就可以确定下界了。
在枚举上界时,有的答案我们不必重复去算。

假设我们已经确定R的上下界为 Rans,R ,设 A[i][j]=k<=Rk=Ranss[k][i][j]
然后再枚举G的上界,确定其范围是 Gans,G ,设 C[i]=k<=Rk=GansC[k][i]
然后枚举B的上界, O(1) 统计答案。当枚举的某个上界增加,把原来的下界部分减去,再加上新的部分。

总的时间复杂度是 O(m3logm) (m是颜色值的最大值)

附上代码的主要部分

bool check(int x)
{
    memset(A,0,sizeof(A));
    for (int i=0;i<x;i++)
        for (int j=0;j<=m;j++)
            for (int k=0;k<=m;k++) A[j][k]+=s[i][j][k];
    for (int r=x;r<=m;r++)
    {
        for (int j=0;j<=m;j++)
            for (int k=0;k<=m;k++) A[j][k]+=s[r][j][k];
        memset(B,0,sizeof(B));
        for (int j=0;j<x;j++)
            for (int k=0;k<=m;k++) B[k]+=A[j][k];
        for (int g=x;g<=m;g++)
        {
            for (int k=0;k<=m;k++) B[k]+=A[g][k];
            int calc=0;
            for (int k=0;k<x;k++) calc+=B[k];
            for (int b=x;b<=m;b++)
            {
                calc+=B[b];
                if (calc>=K) return 1;
                calc-=B[b-x];
            }
            for (int k=0;k<=m;k++) B[k]-=A[g-x][k];
        }
        for (int j=0;j<=m;j++)
            for (int k=0;k<=m;k++) A[j][k]-=s[r-x][j][k];
    }
    return 0;
}

你可能感兴趣的:(【COCI2012】蜡笔)