[Cnoi2020]线形生物
题目描述
有 \(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\) 的出度。
那么有两种情况:
- 直接从 \(i\) 号节点走到 \(i+1\) 号节点
\[E_1=\frac{1}{d_i}\times 1=\frac{1}{d_i}
\]
- 从 \(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]);
}