Min-Max 容斥
简介:
用最小值推最大值的一种容斥。
基本式子:
记
证明如下:
考虑将
证毕。
期望意义下拓展:
例题:
P5643
设节点
那么我们要求
不难写出转移式:
其中
高斯消元!......吗?我们发现转移的依赖关系和原图是一样的,即一棵树,因此我们可以从下到上消元。
具体地,我们可以求出
最后我们还要求
我们可以先将
总时间复杂度
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 20,M = (1 << 18) + 5,mod = 998244353;
int n,q,r,f[M],pc[M],k[N],b[N];
vector <int> v[N];
int ksm(int x,int y)
{
int ret = 1;
while(y)
{
if(y & 1) ret = 1LL * ret * x % mod;
x = 1LL * x * x % mod,y >>= 1;
}
return ret;
}
void dfs(int x,int fa,int s)
{
k[x] = b[x] = 1;
int p = ksm(v[x].size(),mod - 2),inv;
for(int y : v[x])
{
if(y == fa) continue;
dfs(y,x,s);
k[x] = (k[x] - 1LL * p * k[y] % mod + mod) % mod;
b[x] = (b[x] + 1LL * p * b[y] % mod) % mod;
}
if(s & (1 << (x - 1))) k[x] = b[x] = 0;
else
{
inv = ksm(k[x],mod - 2);
if(fa) k[x] = 1LL * p * inv % mod;
else k[x] = 0;
b[x] = 1LL * b[x] * inv % mod;
}
}
int main()
{
scanf("%d%d%d",&n,&q,&r);
for(int i = 1,a,b;i < n;i++)
{
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
for(int i = 1;i < (1 << n);i++)
{
pc[i] = (pc[i >> 1] ^ (i & 1));
dfs(r,0,i);
f[i] = (pc[i] ? b[r] : mod - b[r]);
}
for(int i = 0;i < n;i++)
for(int j = 1;j < (1 << n);j++)
if(j & (1 << i)) f[j] = (f[j] + f[j ^ (1 << i)]) % mod;
for(int i = 1,s,k,p;i <= q;i++)
{
s = 0;
scanf("%d",&k);
while(k--)
{
scanf("%d",&p);
s |= (1 << (p - 1));
}
printf("%d\n",f[s]);
}
return 0;
}