[Cnoi2020]线形生物

Link


题目描述

\(n\) 个点,最开始对于每个点 \(i \in [1,n]\) 都有一条连向 \(i+1\) 的有向边。

现在有额外的 \(m\) 条边 \(u\to v\),有 \(v \leq u\)

求从 \(1\) 号节点开始,走到 \(n+1\) 号节点的期望步数。

解法

容易想到用 \(f_i\) 表示从 \(i\) 号节点走到 \(i+1\) 号节点的期望步数,用 \(d_i\) 表示点 \(i\) 的出度。

那么有两种情况:

  1. 直接从 \(i\) 号节点走到 \(i+1\) 号节点

\[E_1=\frac{1}{d_i}\times 1=\frac{1}{d_i} \]

  1. \(i\) 号节点走到前面的某个点,然后再走回来

\[E_2=\frac{1}{d_i}\times\sum_{v\in son(i)} (1+\sum_{j=v}^{u} f_i) \]

\[f_i=E_1+E_2=1+\frac{1}{d_i}\times\sum_{v\in son(i)} \sum_{j=v}^{u} f_i \]

后面部分显然可以前缀和做差处理

\(s_n=\sum_{i=1}^{n} f_i\)

所以

\[f_i=1+\frac{1}{d_i}\times\sum_{v\in son(i)}(s_{i}-s_{v-1})=1+\frac{1}{d_i}\times\sum_{v\in son(i)}(f_i+s_{i-1}-s_{v-1}) \]

考虑把 \(f_i\) 移到一边

\[f_i=1+\frac{d_i-1}{d_i}\times f_i+\frac{1}{d_i}\times\sum_{v\in son(i)}(s_{i-1}-s_{v-1}) \]

\[\frac{1}{d_i}\times f_i=1+\frac{1}{d_i}\times\sum_{v\in son(i)}(s_{i-1}-s_{v-1}) \]

\[f_i=d_i+\sum_{v\in son(i)}(s_{i-1}-s_{v-1}) \]

可以线性处理了,最终答案是 \(s_n\)

#include<stdio.h>
#define Mod 998244353
#define N 1000007

inline int read(){
    int x=0,flag=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct E{
    int next,to;
}e[N];
int head[N],cnt,s[N],f[N];

inline void add(int id,int to){
    e[++cnt]=(E){head[id],to};
    head[id]=cnt;
}

int id,n,m;
int main(){
    id=read(),n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        add(u,v);
    }
    f[0]=0,s[0]=0;
    for(int i=1;i<=n;i++){
        f[i]=1;
        for(int j=head[i];j;j=e[j].next){
            int k=e[j].to;
            f[i]=((f[i]+s[i-1]-s[k-1]+1)%Mod+Mod)%Mod;
        }
        s[i]=(s[i-1]+f[i])%Mod;
    //    printf("%d %d\n",f[i],s[i]);
    }
    printf("%d",s[n]);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/13734226.html