[大力容斥] 省选模拟赛 4 B. 买买买 buy

来源:互联网 发布:金融分析软件 编辑:程序博客网 时间:2024/05/17 01:23

题目大意

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;}
0 0
原创粉丝点击