旅行的意义(树上的概率dp)

题目描述

为什么有人永远渴望旅行,或许就因为,巧合和温暖会在下一秒蜂拥而至吧。
一直想去旅游的天天决定在即将到来的五一假期中安排一场环游世界的旅行。为此,他已经提前查阅了很多资料,并准备画一张旅游路线图。天天先将所有可能会去的 n 个旅游城市依次编号标记为 1,2,⋯,n。如果从城市 A 到城市 B 有一条直达的铁路线路,他就会在图上画上一条从 A 向 B 的有向线段。因为天天不喜欢把时间浪费在往返的乘车上,因此他设计的旅游地图路线是一个有向无环图。
天天身在 1 号城市,他每到达一个旅游城市都会先花一天的时间游玩当地的旅游景点。接下来他也没有明确的目的地,所以第二天他会随机地选择该城市的一条直达线路,花费一天的时间通往下一个旅游城市。当然,如果这个城市的旅游景点太好玩的话,他可能会选择再逗留一天,但是由于假期有限,他在当前的旅游城市最多只能呆 2 天。例如,当天天在城市 C 时,若城市 C 有 2 条直达线路分别通往城市 A 和城市 B,则在第一天的游玩过后,第二天他有 1/3 的可能会选择继续逗留在城市 C 多游玩一天,但是第三天他一定不会再逗留在城市 C 了;同时他有 1/3 可能会选择立即搭乘直达城市 A 的高铁;他也有 1/3 的可能会选择立即搭乘直达城市 B 的高铁。
当天天把所有的旅游城市都游玩过后,他也就只能结束这段难忘的五一旅行假期了。现在请聪明的你帮天天提前计算一下,他本次旅行时间的期望是多少呢?
容易证明天天旅行时间的期望为 P/Q 的形式,其中 P 和 Q 互质,且 Q≢0 (mod 998244353)。因此答案请以 P⋅Q−1 (mod 998244353) 的形式输出,其中 Q−1 表示 Q 在取模 998244353 下的逆元。

Input

第一行输入一个正整数 T (1≤T≤10),表示数据组数。接下来 T 组数据,每组数据均满足:
第一行输入两个非负整数 n (1≤n≤105) 和 m (0≤m≤105),分别表示天天可能旅行的城市数量 n 和它们之间的直达线路数量 m。
接下来 m 行,每行输入两个正整数 u 和 v (1≤u,v≤n),表示从城市 u 到 v 有一条单向直达线路,保证两个旅游城市之间最多只有 1 条直达线路。

Output

对于每组数据,请输出一个非负整数,表示天天旅行时间的期望,注意换行。

Example

input
2
1 0
2 1
1 2
output
2
499122181

Note

第一组样例只有一个旅游城市。首先,天天会在该城市游玩一天,第二天只剩下一个选择——留下来接着玩一天,再之后他就只能结束旅程了,所以旅游时间的期望是 2。
第二组样例由两个旅游城市,从城市 1 到城市 2 有一条直达的线路。天天首先在城市 1 游玩一天,然后有 1/2 的概率前往城市 2,这将花费 1 天时间乘坐高铁;当然天天也有 1/2 的概率逗留在城市 1 多玩一天,第三天再乘坐高铁前往城市 2。因此刚到达城市 2 时,天天花费的旅行时间期望是 1+[1/2⋅1+1/2⋅(1+1)]=2.5 天。接着天天会在城市 2 先游玩一天,但是接下来他没有其他城市可以去了,只能选择继续逗留一天然后终止旅程,容易算出本次旅程总的时间期望为 4.5 天,即 92=9⋅2−1 (mod 998244353)=499122181。

题目分析
  1. 状态表示:f[i]表示遍历以i节点为根的树所需要的期望天数
  2. 状态计算:我们要分两部分来进行求解
    1)求解天天再在该地游玩一天的期望:f[u]=2+1/(x+1); //x表示u的子节点的个数。前面的2表示:来该地需要一天+到了该地先要玩一天。因此,u==1时没有路上的时间,为f[u]=1+1/(x+1);
    2)求解到别的城市的期望:f[u]=sum(f[v]/x); //即v城市的期望/x。
代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e5+5,mod=998244353;
vector<int> h[N];
LL f[N];
LL kmi(LL a,LL k)		//快速幂求逆元
{
	LL res=1;
	while(k)
	{
		if(k&1) res=res*a%mod;
		a=a*a%mod;
		k>>=1;
	}
	return res;
}
void init(int n)		//初始化:清空f和图
{
	memset(f,0,sizeof f);
	for(int i=0;i<=n;i++) h[i].clear();
}
void dfs(int u)			//遍历图
{
	int x=h[u].size();
	if(u==1) f[u]=1+kmi(x+1,mod-2);		//求再在该地游玩一天的期望
	else f[u]=2+kmi(x+1,mod-2);
	
	for(int i=0;i<h[u].size();i++)		//递归求解到别的城市的期望
	{
		int v=h[u][i];
		if(!f[v]) dfs(v);				//如果v节点前面遍历过了,那么不再遍历
		f[u]=(f[u]+f[v]*kmi(x,mod-2)%mod)%mod;
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--) 
	{
		int n,m;
		scanf("%d %d",&n,&m);
		init(n);
		while(m--)					//建图(有向图)
		{
			int u,v;
			scanf("%d %d",&u,&v);
			h[u].push_back(v);
		}
		dfs(1);						//dfs遍历图dp
		printf("%lld\n",f[1]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值