码迷,mamicode.com
首页 > 其他好文 > 详细

[PKUWC2018]随机游走

时间:2018-09-03 19:32:36      阅读:148      评论:0      收藏:0      [点我收藏+]

标签: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);
    }
}

[PKUWC2018]随机游走

标签:cpp   ==   dig   ali   line   inline   str   printf   display   

原文地址:https://www.cnblogs.com/ytxytx/p/9580073.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!