首页 > 其他分享 >【题解】洛谷-P5643 [PKUWC2018]随机游走

【题解】洛谷-P5643 [PKUWC2018]随机游走

时间:2023-01-27 21:22:24浏览次数:53  
标签:洛谷 int 题解 sum P5643 fa res neq deg

用到了概率期望的很多技巧。

首先要求的是走遍集合中所有节点步数的期望,也就是步数最大值的期望,根据 \(\text{min-max}\) 容斥,有:

\[E\left(\max_{i\in S} x_i\right)=\sum_{T\subseteq S,T\neq \varnothing} (-1)^{|T|-1} E\left(\min_{j\in T} x_j\right) \]

如果我们可以求得最小值的期望,也就是首次到达一个被标记点的期望,就可以通过 \(O(n2^n)\) 的高维前缀和得到最大值的期望。

枚举集合 \(T\),设 \(f_u\) 表示有根树上从 \(u\) 出发首次到达一个被标记点的期望步数。

容易得到式子:

\[\begin{cases} f_u=0&u\in T\\ f_u=\dfrac{\sum_{(u,v)\in E} f_v}{deg_u}+1&u\notin T \end{cases}\]

高斯消元可以求解。

总复杂度 \(O(2^nn^3\log p+nQ)\)。

点击查看代码
inline int q_pow(int x,int p){
    int res=1;
    while(p){
        if(p&1) res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        p>>=1;
    }
    return res;
}

int n,q,rt;
vector<int> E[20];
int a[20][20];
inline void Gauss(){
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(i==j) continue;
            for(int k=i+1;k<=n+1;++k){
                a[j][k]=(a[j][k]-1ll*a[i][k]*q_pow(a[i][i],mod-2)%mod*a[j][i]%mod+mod)%mod;
            }
        }
    }
}
int Emin[(1<<20)+10],Emax[(1<<20)+10];

int main(){
    // freopen("test.in","r",stdin);
    // freopen("test.out","w",stdout);
    n=read(),q=read(),rt=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    for(int s=1;s<(1<<n);++s){
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n+1;++j){
                a[i][j]=0;
            }
        }
        int cnt=0;
        for(int i=1;i<=n;++i){
            if(s&(1<<i-1)) a[i][i]=1,a[i][n+1]=0,++cnt;
            else{
                int now=(mod-q_pow(E[i].size(),mod-2));
                a[i][i]=1,a[i][n+1]=1;
                for(int j:E[i]) a[i][j]=now;
            }
        }
        Gauss();
        a[rt][n+1]=1ll*a[rt][n+1]*q_pow(a[rt][rt],mod-2)%mod;
        if(cnt&1) Emin[s]=a[rt][n+1];
        else Emin[s]=mod-a[rt][n+1];
        Emax[s]=Emin[s];
    }
    for(int i=1;i<=n;++i){
        for(int s=1;s<(1<<n);++s){
            if(!(s&(1<<i-1))) Emax[s|(1<<i-1)]=(Emax[s|(1<<i-1)]+Emax[s])%mod;
        }
    }
    while(q--){
        int k=read(),s=0;
        for(int i=1;i<=k;++i) s|=(1<<read()-1);
        printf("%d\n",Emax[s]);
    }
    return 0;
}

由于矩阵中有意义的值只有 \(O(n)\) 个,高斯消元非常冗余,使用另一种消元方法——系数递推。

在树上表现为对于每个节点,用其父亲的答案去表示该节点的答案,即写成 \(f_u=a_u\times f_{fa}+b_u\) 的形式。

大力推一下:

\[\begin{aligned} f_u&=\dfrac{f_{fa}+\sum_{(u,v)\in E,v\neq fa} f_v}{deg_u}+1\\ deg_u\times f_u&=f_{fa}+\sum_{(u,v)\in E,v\neq fa} (a_v\times f_u+b_v)+deg_u\\ \left(deg_u-\sum_{(u,v)\in E,v\neq fa} a_v\right) f_u&=f_{fa}+deg_u+\sum_{(u,v)\in E,v\neq fa} b_v\\ f_u&=\dfrac{1}{deg_u-\sum_{(u,v)\in E,v\neq fa} a_v} \times f_{fa} + \dfrac{deg_u+\sum_{(u,v)\in E,v\neq fa} b_v}{deg_u-\sum_{(u,v)\in E,v\neq fa} a_v} \end{aligned}\]

于是:

\[\begin{cases} a_u=\dfrac{1}{deg_u-\sum_{(u,v)\in E,v\neq fa} a_v}\\ b_u=\dfrac{deg_u+\sum_{(u,v)\in E,v\neq fa} b_v}{deg_u-\sum_{(u,v)\in E,v\neq fa} a_v} \end{cases}\]

而最终的答案 \(f_{rt}=b_{rt}\)。

总复杂度:\(O(n2^n\log p+nQ)\)。

点击查看代码
inline int q_pow(int x,int p){
    int res=1;
    while(p){
        if(p&1) res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        p>>=1;
    }
    return res;
}

int n,q,rt;
vector<int> E[20];
int a[20],b[20];
int Emin[(1<<20)+10],Emax[(1<<20)+10];

void dfs(int u,int fa,int s){
    int suma=0,sumb=0;
    if(s&(1<<(u-1))) return;
    for(int v:E[u]){
        if(v==fa) continue;
        dfs(v,u,s);
        suma=(suma+a[v])%mod,sumb=(sumb+b[v])%mod;
    }
    // printf("u:%d suma:%d sumb:%d deg:%d\n",u,suma,sumb,(int)E[u].size());
    a[u]=q_pow((E[u].size()-suma+mod)%mod,mod-2),b[u]=1ll*(E[u].size()+sumb)%mod*a[u]%mod;
}

int main(){
    // freopen("test.in","r",stdin);
    // freopen("test.out","w",stdout);
    n=read(),q=read(),rt=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    for(int s=1;s<(1<<n);++s){
        // printf("*%d*\n",s);
        int cnt=0;
        for(int i=1;i<=n;++i){
            a[i]=b[i]=0;
            if(s&(1<<i-1)) ++cnt;
        }
        dfs(rt,0,s);
        if(cnt&1) Emin[s]=b[rt];
        else Emin[s]=mod-b[rt];
        Emax[s]=Emin[s];
        // printf("ans:%d\n",b[rt]);
    }
    for(int i=1;i<=n;++i){
        for(int s=1;s<(1<<n);++s){
            if(!(s&(1<<i-1))) Emax[s|(1<<i-1)]=(Emax[s|(1<<i-1)]+Emax[s])%mod;
        }
    }
    while(q--){
        int k=read(),s=0;
        for(int i=1;i<=k;++i) s|=(1<<read()-1);
        printf("%d\n",Emax[s]);
    }
    return 0;
}

标签:洛谷,int,题解,sum,P5643,fa,res,neq,deg
From: https://www.cnblogs.com/SoyTony/p/Solution_about_Luogu_P5643.html

相关文章

  • 题解 CF1670F【Jee, You See?】
    problem给出常数\(n,z\),令\(F(m)\)表示所有满足以下条件的数列\(\{a_i\}\)的数量:\(\{a_i\}\)有\(n\)项,都是非负整数。它们的和不大于\(m\)。它们的异或和恰......
  • 【ElementPlus】el-menu侧边垂直菜单折叠后图标会消失的问题解决
    首先上代码<template><templatev-for="subIteminmenuList":key="subItem.path"><!--有子菜单--><el-sub-menuv-if="subItem.children&&s......
  • CF908G 题解
    题意传送门给\(x\le10^{700}\),问\(1\)到\(x\)中每个数在各数位排序后得到的数的和。答案模\(10^9+7\)。题解学到一种新鲜的转化方式,来记一下。将\(x\)的位数......
  • Codeforces 708 A-E1题解
    A.Meximization这道题问给一些数,如何让前缀的mex之和最大,那么首先,我们要抬mex的话肯定是要把前面都铺垫完的,所以在i位置确定的时候,i+1自然是越大越好,可以证明i+1的位......
  • Atcoder ABC244E - King Bombee 题解
    原题:AtcoderABC244E-KingBombee题意给你一张图,从\(S\)到\(T\),经过\(k\)条边,经过\(X\)号点偶数次的方案数。做法设\(f_{i,j,k}\)表示经过\(i\)条边,......
  • 洛谷P1835-素数密度
    素数密度题目描述给定区间\([L,R]\)(\(1\leqL\leqR<2^{31}\),\(R-L\leq10^6\)),请计算区间中素数的个数。输入格式第一行,两个正整数\(L\)和\(R\)。输出格式一行......
  • 洛谷 P1208混合牛奶 题解
    一道贪心算法不是很明显的题目,其实一般的递推也可以做。 大体思路:肯定优先购买单价最低的奶农的牛奶,那么就需要先根据牛奶单价进行排序,这里用结构体会更好一点。之后在......
  • CF1780 F.Three Chairs - 题解
    给定数列\(\{a_n\}\),求无序三元组\((i,j,k)\)的数量,满足\(\gcd(\min(a_i,a_j,a_k),\max(a_i,a_j,a_k))=1\),\(n\leq3\cdot10^5,a_i\leq3\cdot10^......
  • ajax跨域访问的问题解决
    在web项目中经常用到在ajax中进行跨域访问,比如在a域中访问b域中的服务,却实现不了。原因是:浏览器为了保证服务器数据的安全,对于这种请求,所给予的权限是较低的,通常只允许调用......
  • 题解 CF1780G【Delicious Dessert】
    SAM板子题。在P3804【模板】后缀自动机(SAM)中,我们已经会求每个等价类(SAM状态)在原串中的出现次数。本题中,我们需要求所有长度能被出现次数整除的子串。我们知道一......