Description:
题解:
出题人本意是考反演,结果没学好期望线性可加。
最近一直在练容斥反演,所以一看就上容斥反演。
设 表示选的水果的集合是S的方案数, 表示选的水果是S’的子集的方案数。
显然 是非常好算的,记 表示拼盘里的水果都属于S’集的拼盘的个数,则
和 的关系是
根据反演套路,设容斥系数为
则
其中 为一个和S’有关的函数,展开 得:
使 ,再把求出的 也乘上 既可以得到 ,后面就简单了。
暴力枚举是会超时的,所以用宽搜+状压dp优化转移。
Code:
#include<bits/stdc++.h>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define fu(a) ((a) & 1 ? -1 : 1)
using namespace std;
const int mo = 998244353;
int n, m, k, x, y, a[20], b[20], a2[20], bz[100005];
ll c[100005][26], g[1 << 18], f[1 << 18][19], h[1 << 18], ans;
ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
}
int main() {
freopen("eat.in", "r", stdin);
freopen("eat.out", "w", stdout);
a2[0] = 1; fo(i, 1, 18) a2[i] = a2[i - 1] * 2;
scanf("%d %d %d", &n, &m, &k);
fo(i, 0, n) {
c[i][0] = 1;
fo(j, 1, min(i, k)) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
fo(i, 1, m) scanf("%d", &a[i]);
fo(i, 1, m) scanf("%d", &b[i]);
fo(i, 1, n) {
scanf("%d", &y);
fo(j, 1, y) {
scanf("%d", &x);
bz[i] |= a2[x - 1];
}
f[bz[i]][0] ++;
}
fo(i, 0, a2[m] - 1) {
fo(j, 0, m - 1) if(f[i][j]) {
fo(k, j, m - 1) if(!(i & a2[k]))
f[i | a2[k]][k + 1] += f[i][j];
}
int s1 = 0; fo(j, 0, m - 1) s1 += (i & a2[j]) > 0;
fo(j, 0, m) g[i] += f[i][j];
g[i] = c[g[i]][k] * fu(s1);
}
memset(f, 0, sizeof f);
fo(i, 0, a2[m] - 1) f[i][0] += g[i];
fo(i, 0, a2[m] - 1) {
fo(j, 0, m - 1) if(f[i][j]) {
fo(k, j, m - 1) if(!(i & a2[k]))
f[i | a2[k]][k + 1] = (f[i | a2[k]][k + 1] + f[i][j]) % mo;
}
int s1 = 0; fo(j, 0, m - 1) s1 += (i & a2[j]) > 0;
fo(j, 0, m) h[i] += f[i][j];
h[i] = (h[i] * fu(s1) % mo + mo) % mo;
ll s = 0;
fo(j, 0, m - 1) if(i & a2[j])
s += a[j + 1]; else s += b[j + 1];
ans = (ans + h[i] * (s % mo)) % mo;
}
ans = ans * ksm(c[n][k], mo - 2) % mo;
printf("%lld", ans);
}