关于此题[ABC391F] K-th Largest Triplet 的一些总结

传送门

题目大意

  • 给定三个序列,使得a[i]b[j]+a[i]c[z]+b[j]c[z],可以合并成N3个元素的新序列,问此序列中第k大的元素是什么。

思路

发现题目给定的k范围最多是5e5。这里提供两种思路:

  • 第一种,直接二分答案。我们可以直接在[1,9e18]中二分答案,由于答案越大,新序列中比答案更大的数越少;答案越小,新序列中比答案更大的数越多。所以答案显然是具有单调性的。关键在于check函数怎么写,我们可以先让三个数组分别降序排序,然后依次枚举三个数组,记getnum(i,j,z)表示a[i]b[j]+a[i]c[z]+b[j]c[z],如果当前枚举到的getnum()比枚举的答案大,那么就让计数器cnt加一,一旦cnt不小于给定的k,就直接返回真,表示答案还可能更大,否则返回假,表示答案只能更小。注意一个小剪枝:由于三个数组已经是从大到小排序的,所以在枚举的第一层循环中,如果getnum(i,1,1)已经小于答案了,就可以直接break;在枚举的第二层循环中,如果getnum(i,j,1)已经小于答案了,也可以直接break。由于至多枚举k次,所以整体复杂度应该是O(mk),其中m表示log9e18,不会超过100。
点击查看代码
#include<bits/stdc++.h>
    
using namespace std;
    
long long t;
const long long N = 2e5 + 10;
long long n,k,a[N],b[N],c[N];

long long getnum(long long i,long long j,long long k) {
    return a[i] * b[j] + a[i] * c[k] + b[j] * c[k];
}

bool check(long long x) {
    long long cnt = 0;
    for(long long i = 1;i <= n;i++) {
        if(getnum(i,1,1) < x) break;
        for(long long j = 1;j <= n;j++) {
            if(getnum(i,j,1) < x) break;
            for(long long z = 1;z <= n;z++) {
                if(getnum(i,j,z) < x) break;
                cnt++;
                if(cnt >= k) return true;
            }
        }
    }
    return false;
}
    
void solve() {
    cin >> n >> k;
    for(long long i = 1;i <= n;i++) cin >> a[i];
    for(long long i = 1;i <= n;i++) cin >> b[i];
    for(long long i = 1;i <= n;i++) cin >> c[i];
    sort(a + 1,a + 1 + n,greater<long long>());
    sort(b + 1,b + 1 + n,greater<long long>());
    sort(c + 1,c + 1 + n,greater<long long>());
    long long l = 1,r = 9e18;
    while(l < r) {
        long long mid = (l + r) >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
        if(r - l == 1) {
            if(check(r)) l = r;
            break;
        }
    }
    cout << l;
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
//  cin >> t;
    t = 1;
    while(t--) solve();
    
    return 0;
}
  • 第二种思路,“暴力”枚举。一样地,我们降序排列三个数组,并三个循环依次枚举i,j,z,放入一个储存数组中,最后再对储存数组找到第k大的数即可。只需要加一个小剪枝:当当前ijz已经大于k了,就可以直接break掉了(貌似很好理解),这样优化下来的时间复杂度是O(klog2k)。注意最后求第k大需要用nth_element(),如果直接排序会超时。
点击查看代码
#include<bits/stdc++.h>
    
using namespace std;
    
long long t;
const long long N = 2e6 + 10;
long long n,k,tot;
long long a[N],b[N],c[N];
vector<long long> cnt;

long long getnum(long long i,long long j,long long k) {
    long long tmp = a[i] * b[j] + a[i] * c[k] + b[j] * c[k];
    return tmp;
}

void solve() {
    cin >> n >> k;
    for(long long i = 1;i <= n;i++) cin >> a[i];
    for(long long i = 1;i <= n;i++) cin >> b[i];
    for(long long i = 1;i <= n;i++) cin >> c[i];
    sort(a + 1,a + 1 + n,greater<long long>());
    sort(b + 1,b + 1 + n,greater<long long>());
    sort(c + 1,c + 1 + n,greater<long long>());
    for(long long i = 1;i <= min(n,k);i++) {
        for(long long j = 1;j <= min(n,k);j++) {
            if(i * j > k) break;
            for(long long z = 1;z <= min(n,k);z++) {
                if(i * j * z > k) break;
                cnt.push_back(getnum(i,j,z));
            }
        }
    }
    nth_element(cnt.begin(),cnt.begin() + k - 1,cnt.end(),greater<long long>());
    cout << cnt[k - 1];
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();
    
    return 0;
}
posted @   孤枕  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
我命令你,喜欢我!