P4799透彻解析

这篇文章仅适用于想要切一道紫的同学。(神犇退散

那么听了课的都知道,这个题是一个仅有蓝题难度的双向搜水紫。

究竟是怎么搜的呢?一起来和小编看看吧

基本思路

思路0

直接枚举每一个子集,再判断是否合法。

浅显易懂,但复杂度O(2n),显然不行。

思路1

把东西分两半,分别枚举子集,在出来的两堆子集里分别挑一个。

这个看上去是优化了,但复杂度仍是O((2n/2)2)=O(2n),没有变化。

思路2

还是把东西分两半,分别枚举子集,但这次只在一堆子集里挑。

每挑出一个,就去另一堆里,找有多少子集能和这个子集配对。

乍一看,O(2n/2×2n/2)(第一个2n/2是枚举子集,第二个2n/2是找配对),没有优化。

但是配对的过程是可以优化的。

下面把找配对的那一堆称为a,挑子集的一堆称为b

现在我们需要完成这个任务:

给定数列 a 和两个数 m,x,求存在多少个 i,使得 ai+xm

这里只需要枚举 b,让 bi=x 即可,关键是如何解决这个问题。

没思路?没事,我们把问题转化成 aimx

给定数列 a 和一个数 p,求 a 中有多少个数 p

(这里 p=mx)。看到这里相信大家都有答案了:

先把 a 排一遍序,在 a 中二分查找 p,下标位置就是答案!

最后,复杂度:

  • 排序:O(2n/2log(2n/2))=O(2n/2n)
  • 二分:O(log(2n/2))=O(n)
  • 二分套枚举 bO(2n/2n)
  • 所以总复杂度:O(2n/2n)

    代码实现

    其实代码能力好的看完上面完全可以写出来了,这里只说一些细节:

  1. 子集枚举:可以用 DFS,也可以用深基的。

DFSlandexie,所以我用的是深基的版本。

  1. 二分查找:这里找的是小于等于,很明显两个STL 都无法直接解决。

但这里稍微思考一下,就可以想到,upper_bound结果-1就是小于等于了。

如果upper_bound查到第 pos 位,那么 0pos1 都是合法的。

那么 0pos1 有几位呢? pos 位。

所以,upper_bound查到第几位,就是有几个数符合要求。

那么代码就出来了:

#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
int n, m, f[101], mid, ans, a[3000001], b[3000001], ca, cb;
void subset(int l, int r, int v[], int &cnt)
{
    int len = r - l, u = 1 << len, t;
    for(int s = 0;s < u;++s)
    {
        t = 0;
        for(int i = 0;i < len;++i)
            if(s & (1 << i)) t += f[i + l];
        v[cnt++] = t;
    }
}
signed main()
{
    cin >> n >> m;mid = n >> 1;
    for(int i = 0;i < n;++i)
        cin >> f[i];
    subset(0, mid, a, ca);
    subset(mid, n, b, cb);
    sort(a, a + ca);
    for(int i = 0;i < cb;++i)
        ans += upper_bound(a, a + ca, m - b[i]) - a;
    cout << ans;
    return 0;
}
posted @   Jijidawang  阅读(21)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示