题目大意
n 种物品,价格 ci 元, k 个人每人有m元
每人会购买若干种物品,可以为空,每种最多一个,总价格不能超过m
总共有多少种不同的购买方案使得每种物品至少被购买了两次
大力容斥一波 我们先枚举哪些是次数小于2的 然后再枚举子集表示哪些是买了一个的 然后把这些个分给某些人 就是个集合的拆分 复杂度是bell数
卡卡常 剪剪枝 就A了…
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int P=998244353;
ll fac[1000005],inv[1000005];
inline void Pre(int n){
fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;
inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(ll)(P-P/i)*inv[P%i]%P;
inv[0]=1; for (int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%P;
}
inline ll Pow(ll a,int b){
ll ret=1;
for (;b;b>>=1,a=a*a%P)
if (b&1)
ret=ret*a%P;
return ret;
}
const int N=12;
int n,m,K,c[N];
ll f[1<<N][1005],pw[1000005];
ll Ans=0;
#define read(x) scanf("%d",&(x))
int S,Tot;
int lst[N],pnt;
int B[N][N],cur,sum[N];
ll Ret;
ll *F;
inline void dfs(int t){
if (t==pnt+1){
ll ret=fac[K]*inv[K-cur]%P;
for (int i=1;i<=cur;i++) ret=ret*F[m-sum[i]]%P;
ret=ret*pw[K-cur]%P;
Ret+=ret;
return;
}
for (int i=1;i<=cur;i++){
B[i][++*B[i]]=t; sum[i]+=c[lst[t]];
if (sum[i]<=m) dfs(t+1);
(*B[i])--; sum[i]-=c[lst[t]];
}
if (cur<K){
++cur;
B[cur][++*B[cur]]=t; sum[cur]+=c[lst[t]];
if (sum[cur]<=m) dfs(t+1);
(*B[cur])--; sum[cur]-=c[lst[t]];
--cur;
}
}
int main(){
freopen("buy.in","r",stdin);
freopen("buy.out","w",stdout);
read(n); read(m); read(K); Tot=(1<<n)-1; Pre(K);
for (int i=1;i<=n;i++) read(c[i]);
for (int s=0;s<(1<<n);s++){
for (int t=s;;t=(--t)&s){
int ret=0;
for (int i=0;i<n;i++)
if (t>>i&1)
ret+=c[i+1];
if (ret<=m) f[s][ret]++;
if (t==0) break;
}
for (int i=1;i<=m;i++) f[s][i]+=f[s][i-1];
}
Ans=Pow(f[(1<<n)-1][m],K);
for (S=1;S<(1<<n);S++){
ll ret=0,cnt=0; F=f[Tot^S];
int s=min(n,K);
pw[K-s]=Pow(F[m],K-s);
for (int i=s-1;i>=0;i--) pw[K-i]=pw[K-i-1]*F[m]%P;
for (int i=0;i<n;i++) if (S>>i&1) cnt++;
for (int s=S;;s=(--s)&S){
pnt=0;
for (int i=0;i<n;i++) if (s>>i&1) lst[++pnt]=i+1;
Ret=0; dfs(1);
ret+=Ret%P;
if (s==0) break;
}
ret%=P;
if (cnt&1) Ans+=P-ret; else Ans+=ret;
//printf("%d\n",ret);
}
printf("%lld\n",Ans%P);
return 0;
}