P4799透彻解析
这篇文章仅适用于想要切一道水紫的同学。(神犇退散)
那么听了课的都知道,这个题是一个仅有蓝题难度的双向搜水紫。
究竟是怎么搜的呢?一起来和小编看看吧
基本思路
思路0
直接枚举每一个子集,再判断是否合法。
浅显易懂,但复杂度
思路1
把东西分两半,分别枚举子集,在出来的两堆子集里分别挑一个。
这个看上去是优化了,但复杂度仍是
思路2
还是把东西分两半,分别枚举子集,但这次只在一堆子集里挑。
每挑出一个,就去另一堆里,找有多少子集能和这个子集配对。
乍一看,
但是配对的过程是可以优化的。
下面把找配对的那一堆称为
现在我们需要完成这个任务:
给定数列
和两个数 ,求存在多少个 ,使得
这里只需要枚举
没思路?没事,我们把问题转化成
给定数列
和一个数 ,求 中有多少个数 。
(这里
先把
最后,复杂度:
- 排序:
- 二分:
- 二分套枚举
: - 所以总复杂度:
代码实现
其实代码能力好的看完上面完全可以写出来了,这里只说一些细节:
- 子集枚举:可以用
,也可以用深基的。
但
- 二分查找:这里找的是小于等于,很明显两个
都无法直接解决。
但这里稍微思考一下,就可以想到,
如果
查到第 位,那么 都是合法的。 那么
有几位呢? 位。 所以,
查到第几位,就是有几个数符合要求。
那么代码就出来了:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具