题目:点击打开链接
题解:点击打开链接
关键:minmax反演。求max转化为求子集的min和
求min时期望dp转移时递推系数即可
对询问记忆化不用枚举子集,好像有个n*2^n的优化 先留坑
#include<bits/stdc++.h>
using namespace std;
#define maxn 500020
#define lowbit(x) (x&(-x))
typedef long long ll;
const ll p = 998244353;
struct node{
int next,to;
}e[50];
int head[maxn],cnt;
int n,q,x,fa[50],N,deg[50],sz[maxn],vis[maxn];
ll mn[maxn],g[50],mx[maxn],A[22],B[22];
inline void adde(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x){
for (int i = head[x] ; i ; i = e[i].next){
if ( e[i].to == fa[x] ) continue;
fa[e[i].to] = x;
dfs(e[i].to);
}
}
inline ll power(ll x,int y){
ll res = 1;
while ( y ){
if ( y & 1 ) res = res * x % p;
x = x * x % p;
y >>= 1;
}
return res;
}
inline void Add(ll &x,ll y){
x += y;
if ( x >= p ) x -= p;
else if ( x < 0 ) x += p;
}
void DP(int x,int S){
if ( S & (1 << (x - 1)) ){ A[x] = B[x] = 0; return; }
ll totA = 0 , totB = 0 , inv;
for (register int i = head[x] ; i ; i = e[i].next){
if ( e[i].to == fa[x] ) continue;
DP(e[i].to,S);
Add(totA,A[e[i].to]) , Add(totB,B[e[i].to]);
}
inv = power(deg[x] - totA,p - 2);
A[x] = inv , B[x] = (totB + deg[x]) * inv % p;
}
void init(){
dfs(x);
N = 1 << n;
for (int i = 1 ; i < N ; i++){
if ( i & (1 << (x - 1)) ) mn[i] = 0;
else{
memset(A,0,sizeof(A)) , memset(B,0,sizeof(B));
DP(x,i);
mn[i] = B[x];
}
sz[i] = sz[i ^ lowbit(i)] + 1;
}
/* for (int i = 1 ; i < N ; i++){
for (register int j = i ; j ; j = (j - 1) & i){
if ( sz[j] & 1 ) Add(mx[i],mn[j]);
else Add(mx[i],-mn[j]);
}
}*/
}
int main(){
scanf("%d %d %d",&n,&q,&x);
for (int i = 1 ; i < n ; i++){
int x,y;
scanf("%d %d",&x,&y);
adde(x,y) , adde(y,x);
deg[x]++ , deg[y]++;
}
init();
while ( q-- ){
int k,cur = 0,x = 0; scanf("%d",&k);
while ( k-- ) scanf("%d",&x) , cur |= 1 << (x - 1);
if ( !vis[cur] ){
for (register int j = cur ; j ; j = (j - 1) & cur){
if ( sz[j] & 1 ) Add(mx[cur],mn[j]);
else Add(mx[cur],-mn[j]);
}
vis[cur] = 1;
}
printf("%lld\n",mx[cur]);
}
return 0;
}