2019ICPC南京网络赛 - D. Robots

题意:

给定一个 n 个点、m 条边的有向无环图,现在要从1号结点沿边走向 n 号结点,每天等概率走向某一邻接结点或者停留在原地,每天花费代价为经历的天数,求到 n 号结点时的期望代价。保证只有 1 号结点无入度,只有 n 号结点无出度。(n <= 1e5, m <= 2e5)

链接:

https://nanti.jisuanke.com/t/41301

题解:

期望dp,f[u] 表示 u 走到 n 号结点的天数期望,g[u] 表示 u 走到 n 号结点的代价期望。记 u -> v,k 为 u 的出度,则 f[u] = f [ u ] + ∑ f [ v ] k + 1 + 1 \cfrac{f[u] + \sum_{}f[v]}{k + 1} + 1 k+1f[u]+f[v]+1,移项整理得 f[u] = ∑ f [ v ] + k + 1 k \cfrac{\sum_{}f[v] + k + 1}{k} kf[v]+k+1,同理 g[u] = ( g [ u ] + f [ u ] + 1 ) + ∑ ( g [ v ] + f [ v ] + 1 ) k + 1 \cfrac{(g[u] + f[u] + 1) + \sum_{}(g[v] + f[v] + 1)}{k + 1} k+1(g[u]+f[u]+1)+(g[v]+f[v]+1),移项整理得 g[u] = f [ u ] + 1 + ∑ ( g [ v ] + f [ v ] + 1 ) k \cfrac{f[u] + 1 + \sum_{}(g[v] + f[v] + 1)}{k} kf[u]+1+(g[v]+f[v]+1),原图反建,从 n 号结点拓扑转移即可。

参考代码:
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

vector<int> G[maxn];
double f[maxn], g[maxn];
int in[maxn], deg[maxn];
int n, m;

void solve(){

	queue<int> q; q.push(n);
	while(!q.empty()){

		int u = q.front(); q.pop();
		for(int i = 0; i < sz(G[u]); ++i){

			int v = G[u][i];
			if(--in[v] == 0) q.push(v);
			f[v] += (f[u] + (deg[v] + 1.0) / deg[v]) / deg[v];
		}
	}
	for(int i = 1; i <= n; ++i) in[i] = deg[i];
	q.push(n);
	while(!q.empty()){

		int u = q.front(); q.pop();
		for(int i = 0; i < sz(G[u]); ++i){

			int v = G[u][i];
			if(--in[v] == 0) q.push(v);
			g[v] += (g[u] + f[u] + 1 + (f[v] + 1) / deg[v]) / deg[v];
		}
	}
}

int main(){

//    ios::sync_with_stdio(0); cin.tie(0);
    int t; scanf("%d", &t);
    while(t--){

        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) G[i].clear(), f[i] = g[i] = in[i] = deg[i] = 0;
        for(int i = 1; i <= m; ++i){

			int u, v; scanf("%d%d", &u, &v);
			G[v].pb(u); ++in[u], ++deg[u];
        }
        solve();
        printf("%.2lf\n", g[1]);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值