[csu oj 2071 Spellcasting]贪心+微分方程

[csu oj 2071 Spellcasting]贪心+微分方程

分类:Greedy math

1. 题目链接

[csu oj 2071 Spellcasting]

2. 题意描述

【题意比较繁琐??】
一开始你有 E E 点能量,能量可以用来造任意数量的元素(实数),并且立刻造完,造完之后加力量,力量的意思是每秒可以产生的能量数目。
现在有N种元素,每种元素每单位需用 ei e i 点能量造,可加 pi p i 点力量,它的父亲是 fi f i
N N 种元素构成树或者森林,每单位的父亲可以使每单位的儿子花费减半。
问最后要达到P点力量需要多少秒钟。
每个节点儿子可能不止一个,所以假如A的儿子是B和C , 那么如果先造1单位A, 那么可以让0.8单位B和0.2单位C的花费各自减半。只要B,C加起来小于1就可以减半。大于1的部分就是原价了。
数据范围
1N1000,1E109,1P109 1 ≤ N ≤ 1000 , 1 ≤ E ≤ 10 9 , 1 ≤ P ≤ 10 9
1ei109,0pi109 1 ≤ e i ≤ 10 9 , 0 ≤ p i ≤ 10 9

3. 解题思路

首先,对于输入的树(森林),我们需要找到一条性价比最高的一条链,初始情况下,把全部的能量全部都分给这条链上,且每个元素分配同样多的能量。然后,就可以发现,其实就可以把这条链看成一个元素了。因为树(森林)的节点最多 N N (1N1000),可以考虑 O(n2) O ( n 2 ) 暴力枚举每条链。
看成一个元素之后,那就是一个很简单的微分方程的求解了呀!
设这个元素,每单位召唤需要 e¯¯¯ e ¯ 点能量,然后每单位该元素每秒可以产生 p¯¯¯ p ¯ 点能量吧。
设时间为 t t 时,总的能量为B(t)吧,那么 B(0)=E B ( 0 ) = E
那么有,

dB(t)=B(t)e¯¯¯p¯¯¯dt d B ( t ) = B ( t ) e ¯ ∗ p ¯ d t

然后可以有:
1B(t)dB(t)=p¯¯¯dte¯¯¯ 1 B ( t ) d B ( t ) = p ¯ d t e ¯

两边同时积分:
1B(t)dB(t)=p¯¯¯e¯¯¯dt ∫ 1 B ( t ) d B ( t ) = ∫ p ¯ e ¯ d t

即:
lnB(t)+=p¯¯¯te¯¯¯ ln ⁡ B ( t ) + 常 数 = p ¯ t e ¯

B(0)=E B ( 0 ) = E 得: =E 常 数 = − E
那么,有
B(t)=Eep¯te¯ B ( t ) = E e p ¯ t e ¯

那么,对 B(t) B ( t ) 求导,就是答案了呀!
B(t)=e¯¯¯p¯¯¯lne¯¯¯Pp¯¯¯E B ( t ) ′ = e ¯ p ¯ ∗ ln ⁡ e ¯ P p ¯ E

最后,还要记得下面两种特判:

  • 一种情况是:这条链初始power就大于等于 P P 了,答案自然为0
  • 还有就是:这条链 p¯¯¯=0 p ¯ = 0 ,也就是说,总的能量不会涨,power永远也到不了 P P <script type="math/tex" id="MathJax-Element-60">P</script>,那么时间就是无限长。

4. 实现代码

#include <ctime>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int inf = 0x3f3f3f3f;
const ll infl = 0x3f3f3f3f3f3f3f3fLL;

template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
void debug() { cout << endl; }
template<typename T, typename ...R>
void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }

const int MAXN = 1005;

int fa[MAXN];
struct Node {
    ll e, p;
} node[MAXN];

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    ll N, E, P;
    while(cin >> N >> E >> P) {
        if(!N && !E && !P) break;
        for(int i = 1; i <= N; ++i) {
            cin >> node[i].e >> node[i].p >> fa[i];
        }
        ll ans = infl;
        for(int u = 1; u <= N; ++u) {
            long double sum_e = 0;
            long double sum_p = 0;
            for(int v = u, cnt = 1; v != 0; v = fa[v], ++cnt) {
                sum_e += node[v].e * 0.5;
                sum_p += node[v].p;
                long double e = (sum_e + node[v].e * 0.5) / cnt;
                long double p = sum_p / cnt;
                if(fabs(p) <= 1e-8) continue; // 时间无限长
                if(P * e <= p * E) { // 初始情况下,就达到要求了
                    ans = 0;
                    break;
                }
                umin(ans, (ll)ceil(e / p * log(e / p * P / E)));
            }
        }
        cout << ans << endl;
    }
#ifdef ___LOCAL_WONZY___
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值