Ralph and Mushrooms (tarjan+dp)

一个综合性的题目,还不错

首先,我们观察,如果一个 x x x 大小的蘑菇能够产生多大的贡献

x + ( x − 1 ) + ( x − 1 − 2 ) + ( x − 1 − 2 − 3 ) +   . .   + ( x − 1 − 2 − 3 − . . . − k ) = ( k + 1 ) x − k ∗ 1 − ( k − 1 ) ∗ 2 − ( k − 2 ) ∗ 3 −   . . .   − 1 ∗ k x+(x-1)+(x-1-2)+(x-1-2-3)+~..~+(x-1-2-3-...-k)\\ =(k+1)x-k*1-(k-1)*2-(k-2)*3-~...~-1*k x+(x1)+(x12)+(x123)+ .. +(x123...k)=(k+1)xk1(k1)2(k2)3 ... 1k

上面那个式子就很好求了,只需要二分 k k k,然后要减的那一坨可以预处理,时间复杂度 O ( l o g n ) O(logn) O(logn)

预处理就这么写:

for (int i = 1, now = 0; i <= 50000; i++) {
	now += i ;
	F[i] = F[i - 1] + now ;
}

很显然能够想到 d p dp dp(或许说最长路也行)

d p [ x ] dp[x] dp[x] 表示到达 x x x 块时的能够采集到的蘑菇的最大数

然后题目坑爹的是不是 D A G DAG DAG,这样 d p dp dp 肯定是会挂菜的

怎么办?

因为是有向图,那么显然可以缩点

不会 t a r j a n tarjan tarjan 求强连通分量的就 G G GG GG

对于一个强连通分量,从任意一个点中出发都可以全部取完

那么这样就可以轻松愉快的 d p dp dp

#include <map>
#include <set>
#include <ctime>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <numeric>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;
#define int long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)
#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)
#define clr(a) memset(a, 0, sizeof(a))
#define ass(a, sum) memset(a, sum, sizeof(a))
#define lowbit(x) (x & -x)
#define all(x) x.begin(), x.end()
#define ub upper_bound
#define lb lower_bound
#define pq priority_queue
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define iv inline void
#define enter cout << endl
#define siz(x) ((int)x.size())
#define file(s) freopen(s".in", "r", stdin), freopen(s."out", "w", stdout)
typedef long long ll ;
typedef unsigned long long ull ;
typedef pair <int, int> pii ;
typedef vector <int> vi ;
typedef vector <pii> vii ;
typedef queue <int> qi ;
typedef queue <pii> qii ;
typedef set <int> si ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
const int N = 1000010 ;
const int INF = 0x3f3f3f3f ;
const int iinf = 1 << 30 ;
const ll linf = 2e18 ;
const int MOD = 1000000007 ;
const double eps = 1e-7 ;
void print(int x) { cout << x << endl ; exit(0) ; }
void PRINT(string x) { cout << x << endl ; exit(0) ; }
void douout(double x){ printf("%lf\n", x + 0.0000000001) ; }

int low[N], dfn[N], vis[N], bl[N], st[N], dp[N], F[N], val[N] ;
int tot, top, scc, n, m, s ;
vii g[N], e[N] ;

void tarjan(int now) {
	low[now] = dfn[now] = ++tot ;
	vis[now] = 1 ;
	st[++top] = now ;
	rep (i, 0, siz(e[now]) - 1) {
		int to = e[now][i].fi ;
		if (!dfn[to]) {
			tarjan(to) ;
			low[now] = min(low[now], low[to]) ;
		} else
		if (vis[to]) low[now] = min(low[now], dfn[to]) ;
	}
	if (low[now] == dfn[now]) {
		scc++ ; int x ;
		do {
			x = st[top--] ;
			vis[x] = 0 ;
			bl[x] = scc ;
		} while (x != now) ;
	}
}

int calc(int c) { // 该点总共能够拿到多少蘑菇
	ll l = 0, r = c ;
	while (l < r) {
		ll m = (l + r + 1) >> 1 ;
		if (m * (m + 1) / 2 <= c) l = m ;
		else r = m - 1 ;
	}
	return c * (l + 1) - F[l] ;
}

int dfs(int now) {
	if (dp[now] != -1) return dp[now] ;
	int res = val[now], tmp = 0 ;
	rep(i, 0, siz(g[now]) - 1) {
		pii to = g[now][i] ;
		tmp = max(tmp, to.se + dfs(to.fi)) ;
	}
	return dp[now] = res + tmp ;
}

signed main(){
	for (int i = 1, now = 0; i <= 50000; i++) { // 预处理数列
		now += i ;
		F[i] = F[i - 1] + now ;
	}
	scanf("%lld%lld", &n, &m) ;
	rep(i, 1, m) {
		int x, y, w ; scanf("%lld%lld%lld", &x, &y, &w) ;
		e[x].pb(mp(y, w)) ;
	}
	scanf("%lld", &s) ;
	rep(i, 1, n) if (!dfn[i]) tarjan(i) ;
	rep(i, 1, n) {
		rep(id, 0, siz(e[i]) - 1) {
			int j = e[i][id].fi, c = e[i][id].se ;
			if (bl[i] == bl[j]) val[bl[i]] += calc(c) ;
			else g[bl[i]].pb(mp(bl[j], c)) ;
		}
	}
	rep(i, 1, scc) dp[i] = -1 ;
	printf("%lld\n", dfs(bl[s])) ;
	return 0 ;
}

/*
写代码时请注意:
	1.ll?数组大小,边界?数据范围?
	2.精度?
	3.特判?
	4.至少做一些
思考提醒:
	1.最大值最小->二分?
	2.可以贪心么?不行dp可以么
	3.可以优化么
	4.维护区间用什么数据结构?
	5.统计方案是用dp?模了么?
	6.逆向思维?
*/


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值