Luogu P2656 采蘑菇

题目描述

小胖和 ZYR 要去 ESQMS 森林采蘑菇。

ESQMS 森林间有 N N N 个小树丛, M M M 条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇。小胖和 ZYR 经过某条小径一次,可以采走这条路上所有的蘑菇。由于 ESQMS 森林是一片神奇的沃土,所以一条路上的蘑菇被采过后,又会长出一些新的蘑菇,数量为原来蘑菇的数量乘上这条路的“恢复系数”,再下取整。

比如,一条路上有 4 4 4 个蘑菇,这条路的“恢复系数”为 0.7 0.7 0.7,则第一~四次经过这条路径所能采到的蘑菇数量分别为 4 , 2 , 1 , 0 4,2,1,0 4,2,1,0

现在,小胖和 ZYR 从 S S S 号小树丛出发,求他们最多能采到多少蘑菇。

输入格式

第一行两个整数, N N N M M M

第二行到第 M + 1 M+1 M+1 行,每行四个数,分别表示一条小路的起点,终点,初始蘑菇数,恢复系数。

M + 2 M+2 M+2 行,一个整数 S S S

输出格式

一行一个整数,表示最多能采到多少蘑菇,保证答案不超过 ( 2 31 − 1 ) (2^{31}-1) (2311)

样例 #1

样例输入 #1

3 3
1 2 4 0.5
1 3 7 0.1
2 3 4 0.6
1

样例输出 #1

8

提示

对于 30 % 30\% 30% 的数据, N ≤ 7 N\le 7 N7 M ≤ 15 M\le15 M15

另有 30 % 30\% 30% 的数据,满足所有“恢复系数”为 0 0 0

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 8 × 1 0 4 1 \le N\le 8\times 10^4 1N8×104 1 ≤ M ≤ 2 × 1 0 5 1\le M\le 2\times 10^5 1M2×105 0 ≤ 恢复系数 ≤ 0.8 0\le\text{恢复系数}\le 0.8 0恢复系数0.8 且最多有一位小数, 1 ≤ S ≤ N 1\le S\le N 1SN

缩点、拓扑序dp - AC

一条边连接的两个点,
在同一个强连通分量内,这条边不限次数地走;
在不同强连通分量内,这条边只能走一次。
不再赘述。

使用long double

struct Edge {
	int u, v, w; db k;
} e[MAXM];
struct _Edge {
	int v, w;
	_Edge(int _v, int _w): v(_v), w(_w){}
};

int n, m, s, dfn[MAXN], low[MAXN], sd[MAXN], js, tot, in[MAXN], dp[MAXN], a[MAXN];
bool vis[MAXN];
vector<int> g[MAXN];
vector<_Edge> ng[MAXN];
stack<int> stk;

void tarjan(int u) {
	dfn[u] = low[u] = ++tot;
	stk.push(u); vis[u] = true;
	for (int v : g[u]) {
		if (!dfn[v]) {
			tarjan(v);
			ckmin(low[u], low[v]);
		}
		else if (vis[v]) {
			ckmin(low[u], low[v]);
		}
	}
	if (dfn[u] == low[u]) {
		int t; ++js;
		while (t = stk.top()) {
			stk.pop();
			sd[t] = js;
			vis[t] = false;
			if (u == t) return;
		}
	}
}

void toposort() {
	queue<int> q;
	for (int i = 1; i <= js; ++i) {
		if (!in[i]) q.push(i);
	}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		stk.push(u);
		for (int i = 0; i < ng[u].size(); ++i) {
			int v = ng[u][i].v;
			if (!--in[v]) q.push(v);
		}
	}
}

int main() {
	setIO("");
	
	cin >> n >> m;
	for (int i = 1; i <= m; ++i) {
		cin >> e[i].u >> e[i].v >> e[i].w >> e[i].k;
		g[e[i].u].push_back(e[i].v);
	}
	cin >> s;
	
	for (int i = 1; i <= n; ++i) {
		if (!dfn[i]) tarjan(i);
	}
	for (int i = 1; i <= m; ++i) {
		if (sd[e[i].u] != sd[e[i].v]) {
			ng[sd[e[i].u]].push_back(_Edge(sd[e[i].v], e[i].w));
			++in[sd[e[i].v]];
		}
		else {
			do {
				a[sd[e[i].u]] += e[i].w;
				e[i].w *= e[i].k;
			} while (e[i].w);
		}
	}
	
	toposort();
	while (!stk.empty()) {
		int u = stk.top(); stk.pop();
		for (int i = 0; i < ng[u].size(); ++i) {
			int v = ng[u][i].v, w = ng[u][i].w;
			ckmax(dp[u], dp[v] + w);
		}
		dp[u] += a[u];
	}
	cout<< dp[sd[s]] << "\n";
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值