蜡笔

本文介绍了一种通过二分查找及三维前缀和的方法来解决从大量三元组中选取特定数量的三元组,使得这些三元组间的最大差异最小的问题。该算法适用于数据规模较大且每个元素取值范围有限的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

有n(n<=100000)个三元组。现在你需要选k个三元组,使者k个三元组两两差异(差异定义为三元组每一项差值的最大值)最小。
三元组每一项都不超过255。

二分

实际上就是挑选k个三元组使得任意两个三元组中任意项对应元素差值的最大值最小。
最大值最小自然就是二分啦。
不过二分对应的是判定性问题,怎么办呢?
我们可以统计最多能挑选多少个,那么如果这个数量大于等于k,挑选k个自然没有问题。
这就转化为判定性问题了。
观察每一项不超过255。我们二分答案ans后,可以枚举三项的上界,并求出下界,然后统计有多少个三元组每一项都在范围内。

三维前缀和

如何统计呢?
我们可以用三维前缀和以及容斥原理。
每一项可以看作一个约束。那么个数:
符合三个约束的-符合两个约束的+符合一个约束的-都不符合的。
这些都可以用三维前缀和得出。
怎么预处理三维前缀和呢?也要用容斥原理。
设cnt[i][j][k]表示三维前缀和。

cnt[i][j][k]+=(cnt[i-1][j][k]+cnt[i][j-1][k]+cnt[i][j][k-1]-cnt[i-1][j-1][k]-cnt[i-1][j][k-1]-cnt[i][j-1][k-1]+cnt[i-1][j-1][k-1]);

注意

防止越界。

参考程序

#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ME CQF
using namespace std;
int num[4][60],sum[4][60][60],cnt[260][260][260];
int i,j,k,l,r,mid,t,n,m,p,q;
bool czy;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
int max(int a,int b){
    if (a>b) return a;else return b;
}
int main(){
    n=read();m=read();
    fo(i,1,n){
        j=read();k=read();l=read();
        j++;
        k++;
        l++;
        cnt[j][k][l]++;
    }
    fo(i,1,256)
        fo(j,1,256)
            fo(k,1,256)
                cnt[i][j][k]+=(cnt[i-1][j][k]+cnt[i][j-1][k]+cnt[i][j][k-1]-cnt[i-1][j-1][k]-cnt[i-1][j][k-1]-cnt[i][j-1][k-1]+cnt[i-1][j-1][k-1]);
    l=0;
    r=255;
    while (l<r){
        mid=(l+r)/2;
        czy=0;
        fo(i,1,256){
            if (czy) break;
            fo(j,1,256){
                if (czy) break;
                fo(k,1,256){
                    q=cnt[i][j][k];
                    q-=cnt[max(i-mid-1,0)][j][k];
                    q-=cnt[i][max(j-mid-1,0)][k];
                    q-=cnt[i][j][max(k-mid-1,0)];
                    q+=cnt[max(i-mid-1,0)][max(j-mid-1,0)][k];
                    q+=cnt[max(i-mid-1,0)][j][max(k-mid-1,0)];
                    q+=cnt[i][max(j-mid-1,0)][max(k-mid-1,0)];
                    q-=cnt[max(i-mid-1,0)][max(j-mid-1,0)][max(k-mid-1,0)];
                    if (q>=m){
                        czy=1;
                        break;
                    }
                }
            }
        }
        if (czy) r=mid;else l=mid+1;
    }
    printf("%d\n",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值