題解-PKUWC2018 隨機遊走

Problem

loj2542git

題意:一棵 \(n\) 個結點的樹,從點 \(x\) 出發,每次等機率隨機選擇一條與所在點相鄰的邊走過去,詢問走完一個集合 \(S\)的指望時間,多組詢問數組

\(n\leq 18,Q\leq 5000\)app

Solution

首先來個\(min-max\)容斥優化

一下是看錯題時想的spa

而後預處理從每一個點開始的到達每一個點的全部集合的指望,\(O(n^22^n)\)卡常可過code

  • 如果這樣,前20pts能夠搞出來了:對於每次詢問在線處理dp數組,利用最值容斥搞事情
  • 30pts的部分是條鏈,能夠對於每一個部分作一次集合前綴後綴預處理
  • 40pts的部分每次只詢問一個點,明顯能夠預處理
  • 事實上70pts的部分最簡單,直接上容斥便可
    接下來是100pts……
    目前最暴力複雜度爲\(O(n^22^n+\sum 2^{k_i})\),若數據隨機,指望狀態下每次僅需計算\(\sum_{i=1}^n\frac {2^i\binom ni}{2^n}\approx 1478\)次,即總共大約須要計算\(18^2\times 2^{18}+5000\times 1478=92324656\leq 10^8\)次,理論可過,但出題人十有八九將其卡掉了(網上說沒卡)

上頭是看看錯題的狀況下想的,實際上全部詢問中起點只可能有一個get

從新理一遍思路:因爲要\(min-max\)反演,即到達集合\(S\)中最後到達點的時間能夠轉化爲\(2^{|S|}\)個子集中最早到達點的時間進行容斥,再容斥便可it

考慮如何求每一個集合中最早到達點的指望時間,由指望的性質可得(設\(f[x]\)表示從\(x\)點出發到達第一個集合點的時間指望,\(v\)表示與\(x\)相連的節點,\(d_i\)表示\(i\)點的度數):io

\[f_x=\frac 1{d_x}\sum_v{(f_v+1)}\]class

因爲須要對全部\(2^n\)個集合都作dp,醬紫求一次須要高斯消元\(n^3\),但總複雜度\(O(n^32^n)\approx 1.5\times 10^9\)必定接受不了

發現目前爲止尚未利用最重要的一個性質:這是一棵樹

要快速求解\(f_x\)數組,就須要必定的優化,因爲這張圖是一棵樹,即每兩點之間的路徑是惟一的,可設\(f_x=Af_{t}+B\)(其中\(t\)\(x\)的父親)

利用上邊的式子列出方程(設\(c\)\(x\)的兒子):

\[f_x=\frac 1{d_x}\bigl[f_t+1+\sum_{x\rightarrow c}(f_c+1)\bigr]\]

因爲兒子加父親節點一共\(d_x\)個,即

\[f_x=\frac 1{d_x}(f_t+\sum_{x\rightarrow c}f_c)+1\]

咱們要解出\(A,B\),因此須要將\(f_x=Af_t+B\)代入式子

\[f_x=\frac 1{d_x}\bigl[f_t+\sum_{x\rightarrow c}(A_cf_x+B_c)\bigr]+1\]

\[(d_x-\sum_{x\rightarrow c} A_c)f_x=f_t+\sum_{x\rightarrow c}B_c+d_x\]

\[f_x=\frac 1{d_x-\sum A_c}f_t+\frac {\sum B_c+d_x}{d_x-\sum A_c}\]

解得:
\[\left\{ \begin{aligned} A_x & = \frac 1{d_x-\sum A_c} \\ B_x & = \frac {\sum B_c+d_x}{d_x-\sum A_c} \end{aligned} \right.\]

再加上集合\(S\)內的點\(x\)知足\(A_x=B_x=0\)

而後對於每一個集合 \(S\) 就能夠 \(O(n)\) 地求出從任意點出發到達第一個集合內點的指望時間

這樣詢問就能夠愉悅地 \(\mathrm{min-max}\) 容斥了

\(O(1)\) 查詢的話只須要高維前綴和一下便可,因而乎總時間複雜度爲 \(O(n2^n+Q)\)

upd:好像還要算上求逆元,複雜度 \(O(n2^n\log p+Q)\),算出來大概 \(1.5e8\),但因爲枚舉集合後一旦走到集合就遞迴,再算上快速冪的小常數,徹底可過(複雜度跑不滿,跑得最滿的狀況是菊花圖,但即使是菊花圖,常數最大也就 \(\frac 12\)

Code

#include <cstdio>
#include <cctype>

inline void read(int&x){
    char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
}

const int N=19,M=1<<18,p=998244353;
struct Edge{int v,nxt;}a[N*N];
int k[N],b[N],deg[N],head[N];
int f[M],bit[M],n,Q,st,_;

inline int qpow(int A,int B){
    int res(1);
    while(B){
        if(B&1)res=1ll*res*A%p;
        A=1ll*A*A%p,B>>=1;
    }return res;
}

inline void pls(int&A,int B){A=A+B<p?A+B:A+B-p;}
inline void dec(int&A,int B){A=A-B<0?A-B+p:A-B;}

void dfs(int x,int las,int lim){
    if(lim&(1<<x-1)){k[x]=b[x]=0;return ;}
    k[x]=b[x]=deg[x];
    for(int i=head[x];i;i=a[i].nxt)
        if(a[i].v!=las){
            dfs(a[i].v,x,lim);
            dec(k[x],k[a[i].v]);
            pls(b[x],b[a[i].v]);
        }
    k[x]=qpow(k[x],p-2);
    b[x]=1ll*b[x]*k[x]%p;
}

int main(){
    read(n),read(Q);read(st);int lim=1<<n;
    for(int i=1,x,y;i<n;++i){
        read(x),read(y),++deg[x],++deg[y];
        a[++_].v=y,a[_].nxt=head[x],head[x]=_;
        a[++_].v=x,a[_].nxt=head[y],head[y]=_;
    }
    for(int S=1;S<lim;++S){
        bit[S]=bit[S>>1]+(S&1);
        dfs(st,0,S);
        f[S]=(bit[S]&1?b[st]:p-b[st]);
    }
    for(int i=1;i<lim;i<<=1)
    for(int j=0;j<lim;++j)
        if(i&j)pls(f[j],f[i^j]);
    
    int t,x,s;
    while(Q--){
        read(t),s=0;
        while(t--)read(x),s|=1<<x-1;
        printf("%d\n",f[s]);
    }
    return 0;
}
相關文章
相關標籤/搜索