「PKUWC2018」随机游走

LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp)

其实不是很难啦

min-max容斥既视感

设f[x]表示从x走到S中第一个点的期望步数

f[x]=1/d[x]*(f[fa[x]])+∑1/d[x]*(f[ch[x]])+1

这个有环

利用f[x]=A*f[fa[x]]+B的套路代换

得到A,B的递推式

对于x=rt,f[fa[x]]=0,所以f[x]=Bx了

对于叶子,结果恰好也是A=1,B=1

可以2^n枚举S,再O(nlogn)log有逆元,得到答案

对于Q,直接2^n枚举子集

O((nlogn+Q)*2^n))

因为2^n跑不满,可以过。

或者,预处理答案,O(1)回答

O((nlogn)+3^n+Q))

代码(法一):

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=20;
const int mod=998244353;
int n,q;
int rt;
int qm(int x,int y){
    int ret=1;
    while(y){
        if(y&1) ret=(ll)ret*x%mod;
        x=(ll)x*x%mod;
        y>>=1;
    }
    return ret;
}
int A[N],B[N];
int ans[1<<18];
int sz[1<<18];
int S;
int d[N];
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
void dfs(int x,int fa){
    //A[x]=0;B[x]=0;
    if(S&(1<<(x-1))) {
        A[x]=0,B[x]=0;
        return;
    }
    int sb=0,sa=0;
    int son=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        ++son;
        dfs(y,x);
        sb=(sb+B[y])%mod;
        sa=(sa+A[y])%mod;
    }
    
    if(son){
        A[x]=qm((d[x]-sa+mod)%mod,mod-2);
        B[x]=((ll)sb+d[x])*qm((d[x]-sa+mod)%mod,mod-2)%mod;
    }else{
        A[x]=1;
        B[x]=1;
    }
}
int main(){
    rd(n);rd(q);rd(rt);
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        ++d[x];++d[y];
        add(x,y);add(y,x);
    }
    for(S=1;S<(1<<n);++S){
        sz[S]=sz[S>>1]+(S&1);
        dfs(rt,0);
        ans[S]=B[rt];
    //    cout<<" S "<<S<<" : "<<ans[S]<<" A "<<A[rt]<<endl;
    }
    int k;
    while(q--){
        S=0;
        rd(k);
        for(reg i=1;i<=k;++i){
            rd(x);
            S|=(1<<(x-1));
        }
        ll op=0;
        for(int tmp=S;tmp;tmp=S&(tmp-1)){
            if(sz[tmp]&1){
                op=((ll)op+ans[tmp])%mod;
            }else{
                op=((ll)op-ans[tmp]+mod)%mod;
            }
        }
        printf("%lld
",op);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/1/20 21:36:25
*/
原文地址:https://www.cnblogs.com/Miracevin/p/10296423.html