一个综合性的题目,还不错
首先,我们观察,如果一个 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+(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
上面那个式子就很好求了,只需要二分 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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.逆向思维?
*/