1. 程式人生 > >Loj #2542. 「PKUWC2018」隨機遊走

Loj #2542. 「PKUWC2018」隨機遊走

line ans 範圍 com 格式 space 相同 span 出發

Loj #2542. 「PKUWC2018」隨機遊走

題目描述

給定一棵 \(n\) 個結點的樹,你從點 \(x\) 出發,每次等概率隨機選擇一條與所在點相鄰的邊走過去。

\(Q\) 次詢問,每次詢問給定一個集合 \(S\),求如果從 \(x\) 出發一直隨機遊走,直到點集 \(S\) 中所有點都至少經過一次的話,期望遊走幾步。

特別地,點 \(x\)(即起點)視為一開始就被經過了一次。

答案對 $998244353 $ 取模。

輸入格式

第一行三個正整數 \(n,Q,x\)

接下來 \(n-1\) 行,每行兩個正整數 \((u,v)\) 描述一條樹邊。

接下來 \(Q\) 行,每行第一個數 \(k\)

表示集合大小,接下來 \(k\) 個互不相同的數表示集合 \(S\)

輸出格式

輸出 \(Q\) 行,每行一個非負整數表示答案。

數據範圍與提示

對於 \(20\%\) 的數據,有 \(1\leq n,Q\leq 5\)

另有 \(10\%\) 的數據,滿足給定的樹是一條鏈。

另有 \(10\%\) 的數據,滿足對於所有詢問有 \(k=1\)

另有 \(30\%\) 的數據,滿足 \(1\leq n\leq 10 ,Q=1\)

對於 \(100\%\) 的數據,有 \(1\leq n\leq 18\)\(1\leq Q\leq 5000\)\(1\leq k\leq n\)

Orz

首先根據\(\min-\max\)

反演我們知道:
\[ \max(S)=\sum_{T\subseteq S}(-1)^{|T|-1}\min(T) \]
\(f_{v,S}\)表示從\(v\)出發,經過\(S\)中至少一個節點的期望步數。

如果\(v\in S\)\(f_{v,S}=0\),否則:
\[ f_v=1+\frac{1}{d_v}f_{fa}+\frac{1}{d_v}\sum f(u)\\]
然後這是顆樹,我們可以將\(DP\)方程移項變成只與\(fa\)\(f\)值個一個常數有關。

設:
\[ f(v)=A_v*f_{fa}+B_v\\]
帶回去化簡:
\[ f_v=1+\frac{1}{d_v}f_{fa}+\frac{1}{d_v}\sum (A_u*f_v+B_u)\(d_v-\sum A_u)*f_v=d_v+f_{fa}+\sum B_u\f_v=\frac{1}{d_v-\sum A_u}*f_{fa}+\frac{d_v+\sum B_u}{d_v-\sum A_u} \]


得到:
\[ A_v=\frac{1}{d_v-\sum A_u},B_v=\frac{d_v+\sum B_u}{d_v-\sum A_u} \]
代碼:

#include<bits/stdc++.h>
#define ll long long
#define N 19

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=998244353;
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

int n;
int X,m;
struct road {int to,nxt;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}

ll w[N];
int d[N];
ll A[N],B[N],f[N];
int tag[N];

void dfs(int v,int fa) {
    A[v]=B[v]=0;
    ll sumA=0,sumB=0;
    for(int i=h[v];i;i=s[i].nxt) {
        int to=s[i].to;
        if(to==fa) continue ;
        dfs(to,v);
        sumA=(sumA+A[to])%mod;
        sumB=(sumB+B[to])%mod;
    }
    if(tag[v]) A[v]=B[v]=0;
    else A[v]=ksm(d[v]-sumA+mod,mod-2),B[v]=(d[v]+sumB)*ksm(d[v]-sumA+mod,mod-2)%mod;
}

void dfs2(int v,int fa) {
    f[v]=(A[v]*f[fa]+B[v])%mod;
    for(int i=h[v];i;i=s[i].nxt) {
        int to=s[i].to;
        if(to==fa) continue ;
        dfs2(to,v);
    }
}
int mn[1<<N];
int main() {
    n=Get(),m=Get(),X=Get();
    int a,b;
    for(int i=1;i<n;i++) {
        a=Get(),b=Get();
        add(a,b),add(b,a);
        d[a]++,d[b]++;
    }
    for(int S=1;S<1<<n;S++) {
        for(int i=1;i<=n;i++) if(S>>i-1&1) tag[i]=1;
        dfs(1,0),dfs2(1,0);
        mn[S]=f[X];
        for(int i=1;i<=n;i++) if(S>>i-1&1) tag[i]=0;
    }
    for(int S=1;S<1<<n;S++) {
        int cnt=0;
        for(int i=0;i<n;i++) cnt+=S>>i&1;
        if(!(cnt&1)) mn[S]=(mod-mn[S])%mod;
    }
    for(int i=0;i<n;i++) {
        for(int S=1;S<1<<n;S++) {
            if(S>>i&1) mn[S]=(mn[S]+mn[S^(1<<i)]+mod)%mod;
        }
    }
    while(m--) {
        int k=Get();
        int sta=0;
        while(k--) sta|=1<<Get()-1;
        cout<<mn[sta]<<"\n";
    }
    return 0;
}

Loj #2542. 「PKUWC2018」隨機遊走