488 字
2 分钟
Codeforces 525E
2026-02-11

题目链接#

Anya and Cubes

题目大意#

给定 nn  个数,kk 次阶乘的机会,以及最终结果 ss。 每个数可以选择不选、选择原数、选择数的阶乘三个选项。但最多不能超过 kk 个数选择阶乘。

要求出最终组合得到结果 ss 的方案数。

题解#

一开始以为是 dp 来的,但是 ss 的范围太大了,,没有办法做 dp 。

用平常的搜索也会导致时间复杂度太高。

最后实在没办法了,去看了题解。用到了 meet-in-the-middle。没办法了,去看了题解。用到了 meet-in-the-middle。折半搜索。好吧说实话这是我学了之后第一次用这个方法,以至于我根本没有想到。

折半搜索适用于数据范围小,但是又不能直接使用暴搜的情况。

暴搜的复杂度通常是指数级的,而折半搜索则是把暴搜分为前后两个过程,这样可以把指数减半,即从 O(ab)O(a^b) 降低到 O(ab/2)O(a^{b/2})

方法有了之后就通畅了。鉴于阶乘的增长速度,我们不需要计算很多阶乘,20!20! 就足够了。然后就是正常的 DFS,设定出界条件,以及接下来的 DFS。和普通 DFS 不同的是,折半搜索分为两部分,我们需要把第一部分的答案存起来,然后再在第二部分的搜索中借以求出最终答案。

例题#

P2962 [USACO09NOV] Lights G

AC 代码#

#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
int ppp[20];
void init(){
int i=1;
ppp[0]=1;
for(;i<20;i++){
ppp[i]=ppp[i-1]*i;
}
}
int n,k,s,ans;
vector<int> v;
vector<unordered_map<int,int>> m;
void dfs1(int now,int end,int S,int used){
if(used>k)return;
if(S>s)return;
if(now>end){
m[used][S]++;
return;
}
dfs1(now+1,end,S,used);
dfs1(now+1,end,S+v[now],used);
if(v[now]<=18)dfs1(now+1,end,S+ppp[v[now]],used+1);
}
void dfs2(int now,int end,int S,int used){
if(used>k)return;
if(S>s)return;
if(now>end){
for(int i=0;i<=k-used;i++)
ans+=m[i][s-S];
return;
}
dfs2(now+1,end,S,used);
dfs2(now+1,end,S+v[now],used);
if(v[now]<=18)dfs2(now+1,end,S+ppp[v[now]],used+1);
}
signed main(){
std::ios::sync_with_stdio(false);std::cin.tie(0);
init();
ppp[0]=0;
cin>>n>>k>>s;
m.resize(n+1);
v.resize(n);for(int i=0;i<n;i++)cin>>v[i];
sort(v.begin(),v.end());
dfs1(0,n/2,0,0);
dfs2(n/2+1,n-1,0,0);
cout<<ans<<endl;
return 0;
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Codeforces 525E
https://leaf146.cn/posts/codeforces-525e/
作者
LeAf146
发布于
2026-02-11
许可协议
MIT

部分信息可能已经过时