CF525E Anya and Cubes

CF525E Anya and Cubes

洛谷CF525E Anya and Cubes

题目大意

给定一个长度为 n n n的序列 a i a_i ai。你可以进行不超过 k k k次修改,使序列中的一个数 a i a_i ai变为 a i ! a_i! ai!,同一个 i i i a i a_i ai只能被修改一次。求进行不超过 k k k次修改后选择若干个数使得这些数的和为 S S S的方案数。注意这里的方案数是修改+选择的方案数,两种方案不同当且仅当存在一个 i i i在一种修改中被选择而在另一种修改中没被选择,或者存在一个 i i i使得 i i i在两种修改中都被选择且在一种方案中被修改而在另一种方案中没被修改。

1 ≤ n ≤ 25 , 0 ≤ a i ≤ 1 0 9 1\leq n\leq 25,0\leq a_i\leq 10^9 1n25,0ai109


题解

按题目要求,其实就是被修改的 a i a_i ai必须选择(因为如果不选择,则修改和不修改被视作同一种情况),那么每个 a i a_i ai有三种情况:

  • 没有被选择
  • 被选择了但没有被修改
  • 被选择了且被修改

如果直接枚举所有情况的话是 O ( 3 n ) O(3^n) O(3n)的,下面考虑优化。

考虑用折半搜索,先对前 ⌊ n 2 ⌋ \lfloor\dfrac n2\rfloor 2n个数枚举所有情况并根据选择的数之和以及修改次数存储在 unordered map \text{unordered map} unordered map中,然后对后 n − ⌊ n 2 ⌋ n-\lfloor\dfrac n2\rfloor n2n个数枚举所有情况,对于每种情况,在 unordered map \text{unordered map} unordered map O ( k ) O(k) O(k)查找能与其匹配成和为 S S S且总修改次数不超过 k k k的方案数并计算贡献,总时间复杂度为 O ( k × 3 n / 2 ) O(k\times 3^{n/2}) O(k×3n/2)

注意因为选择的数的和不超过 S S S,所以只有当 a i ≤ 20 a_i\leq 20 ai20时才能修改(否则修改后会超过 S S S)。

code

#include
using namespace std;
int n,k,a[30];
long long S,ans=0,jc[25];
unordered_map<long long,int>v[30];
void dfs1(int t,int tp,long long now,int hv){
	if(now>S||hv>k) return;
	if(t==tp+1){
		++v[hv][now];
		return;
	}
	dfs1(t+1,tp,now,hv);
	dfs1(t+1,tp,now+a[t],hv);
	if(a[t]<=20) dfs1(t+1,tp,now+jc[a[t]],hv+1);
}
void dfs2(int t,int tp,long long now,int hv){
	if(now>S||hv>k) return;
	if(t==tp+1){
		for(int i=0;i<=k-hv;i++) ans+=v[i][S-now];
		return;
	}
	dfs2(t+1,tp,now,hv);
	dfs2(t+1,tp,now+a[t],hv);
	if(a[t]<=20) dfs2(t+1,tp,now+jc[a[t]],hv+1);
}
int main()
{
	scanf("%d%d%lld",&n,&k,&S);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	jc[0]=1;
	for(int i=1;i<=20;i++) jc[i]=jc[i-1]*i;
	dfs1(1,n/2,0,0);
	dfs2(n/2+1,n,0,0);
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(题解,题解,c++)