标签:cpp == dig ali line inline str printf display
给你一颗树和根,每次询问从根出发,随机走到有连边的结点,经过集合S中所有结点的步数期望
\(1 \leq n \leq 18,1 \leq Q \leq 5000\)
首先我们要求出所有集合\(S\)经过至少一个\(S\)中的点的步数期望(为最值反演铺垫)
令\(dp_{S,i}\)表示以i为起点,要经过集合S中至少一个点的步数期望
那么\(dp_{S,i}=\frac{1}{dig_i}(dp_{S,fa_i}+1+\sum_{v\in son}{dp_{S,v}+1})\),\(dig\)表示\(i\)的度数
根据套路,我们假设\(dp_{S,i}=A_idp_{S,fa_i}+B_i\)
\[ \begin{align} dp_{S,i} & =\frac{1}{dig_i}(dp_{S,fa_i}+1+\sum_{v\in son}{dp_{S,v}+1}) \ & =\frac{1}{dig_i}(dp_{S,fa_i}+1+\sum_{v\in son}{A_vdp_{S,i}+B_v+1}) \ & =\frac{1}{dig_i}(dp_{S,fa_i}+dp_{S,i}\sum_{v\in son}{A_v}+\sum_{v\in son}{B_v})+1 \ & =\frac{1}{dig_i}(dp_{S,fa_i}+dp_{S,i}sigA+sigB)+1 \ dig_idp_{S,i} & =dp_{S,fa_i}+dp_{S,i}sigA+sigB+1 \ (dig_i-sigA)dp_{S,i} & =dp_{S,fa_i}+sigB+1 \ dp_{S,i} & =\frac{1}{dig_i-sigA}dp_{S,fa_i}+\frac{sigB+1}{dig_i-sigA} \ \end{align} \]
于是我们得到了
\[ A_i=\frac{1}{dig_i-sigA} \]
\[ B_i=\frac{sigB+1}{dig_i-sigA} \]
显然如果\(i\in S\)那么\(A_i=B_i=0\)
然后A,B是可以在树上dp的(因为式子只和它的孩子有关)
那么我们要求的
\[ \begin{align} dp_{S,root} & = A_{root}dp_{S,fa_{root}}+B_{root} \ & = B_{root} \ \end{align} \]
于是每次询问集合S,只需要进行最值反演,\(max(S)=\sum_{S‘\subseteq S}{dp_{S‘,root}\cdot (-1)^{|S‘|+1}}\)
\(max(S)\)即为每次询问的答案
#include<iostream>
#include<cstdio>
const int P=998244353;
struct edge{
int to,next;
}E[60];
int H[30],tot;
void add_edge(int a,int b){
E[++tot]=(edge){b,H[a]};H[a]=tot;
E[++tot]=(edge){a,H[b]};H[b]=tot;
}
int n,Q,St,x,y,k,ch[30];
long long ans;
long long A[30],B[30],dp[3000000],dig[30];
long long inv(long long k){return (k==1)?(1):((P-P/k)*inv(P%k)%P);}
void dfs(int S,int now,int fa){
if ((1<<(now-1))&S){
A[now]=B[now]=0;
return;
}
long long sigA=0,sigB=0;
for (int i=H[now];i;i=E[i].next){
if (E[i].to==fa) continue;
dfs(S,E[i].to,now);
sigA=(sigA+A[E[i].to])%P;
sigB=(sigB+B[E[i].to])%P;
}
long long Inv=(dig[now]==sigA)?(0):(inv(dig[now]-sigA));
A[now]=Inv;
B[now]=(sigB+dig[now])%P*Inv%P;
}
void Min_Max(int now,int S,int f){
if (now>k){ans=(ans+f*dp[S]%P+P)%P;return;}
Min_Max(now+1,S,f);
Min_Max(now+1,S|(1<<(ch[now]-1)),-f);
}
int main(){
scanf("%d%d%d",&n,&Q,&St);
for (int i=1;i<n;i++){
scanf("%d%d",&x,&y);
dig[x]++;dig[y]++;
add_edge(x,y);
}
for (int i=0;i<(1<<n);i++){
dfs(i,St,0);
dp[i]=B[St];
}
for (int i=1;i<=Q;i++){
scanf("%d",&k);
for (int j=1;j<=k;j++) scanf("%d",&ch[j]);
ans=0;
Min_Max(1,0,-1);
printf("%lld\n",ans);
}
}
标签:cpp == dig ali line inline str printf display
原文地址:https://www.cnblogs.com/ytxytx/p/9580073.html