Description
Input
第一行三个数
n,m,k
n
,
m
,
k
第二行m个数
ai
a
i
第三行m个数
bi
b
i
接下来n行,每行第一个数
k
k
表示这一份水果拼盘包含的水果种数,接下来个数表示这一份水果拼盘包含哪些种类的水果,保证数字不会重复。
Output
一行一个数,表示答案。
Sample Input
4 5 2
8 8 8 7 0
4 8 6 6 2
1 2
5 4 3 1 5 2
5 2 4 5 3 1
2 1 2
Sample Output
831870325
Data Constraint
分析:
考场上想到fwt,然后有一个地方没取模,果断65分。
显然我们设
fk(x)
f
k
(
x
)
为取
k
k
个水果盘且不重复(顺序任意)的方案数。显然
可以看做是前面的一个 k−1 k − 1 排列,给后面加一个数,算重只有 k−1 k − 1 种情况。
这个显然是多项式卷积, fwt f w t 解决,减法在 fwt f w t 变换后也就可以直接减的。
最后因为总共有排列数为 n!(n−k)! n ! ( n − k ) ! ,统计答案即可。
因为不同排列贡献相同,所以就相当于组合了。
其实还有一种超级简单的做法。考虑每一位,这一位的贡献就是一个组合数就解决了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=3e5+7;
const LL mod=998244353;
using namespace std;
LL n,m,k,num,s,x;
LL f[maxn],val[maxn],g[maxn];
LL a[27],b[27];
LL inv,jc;
LL power(LL x,LL y)
{
if (y==1) return x;
LL c=power(x,y/2);
c=(c*c)%mod;
if (y%2) c=(c*x)%mod;
return c;
}
void fwt(LL *a,LL l,LL r)
{
if (l==r) return;
LL n=(r-l+1)/2,mid=l+n;
fwt(a,l,mid-1);
fwt(a,mid,r);
for (LL i=l;i<mid;i++)
{
LL u=a[i],v=a[i+n];
a[i+n]=(u+v)%mod;
}
}
void dwt(LL *a,LL l,LL r)
{
if (l==r) return;
LL n=(r-l+1)/2,mid=l+n;
dwt(a,l,mid-1);
dwt(a,mid,r);
for (LL i=l;i<mid;i++)
{
LL u=a[i],v=a[i+n];
a[i+n]=(v-u+mod)%mod;
}
}
int main()
{
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&k);
for (LL i=1;i<=m;i++) scanf("%lld",&a[i]);
for (LL i=1;i<=m;i++) scanf("%lld",&b[i]);
for (LL i=1;i<=n;i++)
{
scanf("%lld",&num);
s=0;
for (LL j=1;j<=num;j++)
{
scanf("%lld",&x);
s^=1<<(x-1);
}
f[s]++;
}
for (LL i=1;i<(1<<m);i++)
{
for (LL j=1;j<=m;j++)
{
if (i&(1<<(j-1))) val[i]+=a[j];
else val[i]+=b[j];
}
}
jc=1;
for (LL i=n-k+1;i<=n;i++) jc=(jc*i)%mod;
inv=power(jc,mod-2);
fwt(f,0,(1<<m)-1);
for (LL i=0;i<(1<<m);i++) g[i]=f[i];
for (LL j=2;j<=k;j++)
{
for (LL i=0;i<(1<<m);i++) f[i]=(f[i]*g[i]%mod+mod-f[i]*(j-1)%mod)%mod;
}
dwt(f,0,(1<<m)-1);
LL ans=0;
for (LL i=0;i<(1<<m);i++)
ans=(ans+f[i]*inv%mod*val[i]%mod)%mod;
printf("%lld",ans);
}