CF327E

状压好题。

题意

给你一个长度为n的正整数序列 \(S\),再有 \(k\) 个正整数。
求有多少种 \(S\) 的排列方式使得其前缀和不会成为那 \(k\) 个数里的任意一个。答案对 \(1e9+7\) 取模。
\(0 \le k \le 2,1 \le n \le 24\)

题解

因为 \(n \le 24\),明显是状压。

假设 \(f_i\) 表示在状态为 \(i\) 的情况下的组合方法数,则答案为 \(f_{2^n-1}\)

定义一个数组 \(a\),其中 \(a_i\) 存储在情况 \(i\) 下的元素之和,很容易理解。

对于一个新的 \(a_i\)\(a_i = a_{lowbit(i)} + a_{i \, xor \, lowbit(i)}\)

考虑如何转移。

对于 \(f_i\) 可以由 \(f_j\) 转移而来,其中 \(j\)\(i\) 去掉一个元素得来的。

那么状态转移方程就是 \(f_i = \sum f_j\),在循环内枚举每一位的情况即可。

代码

#include <bits/stdc++.h>
#define ll long long
#define lb(x) (x&(-x))
using namespace std;const ll N=24,mod=1e9+7;
int a[1<<N],f[1<<N],n,k,b[N],lim;
int main(){
    cin>>n;lim=(1<<n)-1;for(int i=1;i<=lim;i<<=1)cin>>a[i];
    cin>>k;for(int i=1;i<=k;i++)cin>>b[i];f[0]=1;
    for(int i=0;i<=lim;i++){
	a[i]=a[i^lb(i)]+a[lb(i)];
	if(a[i]!=b[1]&&a[i]!=b[2])
	    for(int j=i;j;j^=lb(j))
                f[i]=(f[i^lb(j)]+f[i])%mod;
    }cout<<f[lim];return 0;
}
posted @ 2022-03-08 12:34  AIskeleton  阅读(29)  评论(0编辑  收藏  举报