哈理工第八届校团队赛I乘胜追击

Description

三国时期,群雄割据,战乱频繁。公元228年,孙权派番阳太守周鲂诱骗曹休派兵前来,曹休果然中计。待孙权任陆逊为大都督,朱桓、全琮为左、右督,各领兵三万人迎击曹休时,曹方知被欺。但其仗恃兵力占优,仍决定与吴国交战。彼时陆逊所率大军与与曹休所率大军于石亭展开激战。陆逊探得曹休于长江边共驻扎n座军营,相互以粮道连接,其中第1座军营为大本营。当第i(i>=2)座军营被攻陷时,营中残兵败将将沿粮道向第f[i]座军营逃窜。陆逊极善用兵,常人用a[i]的兵力方能攻下第i座军营,而陆逊仅需a[i]-d[i]的兵力即可攻下。倘给陆以a[i]的兵力,则陆不仅能攻下第i座军营,还可乘胜追击,沿着军士逃跑方向攻下第f[i]座军营。陆逊善用兵如此,故而仅用了最少的兵力便大破曹休,攻下所有军营,生擒一万余人,缴获牛马驴骡车辆上万。试问陆逊于石亭之战中共用兵力几何?(以上史料来自百度百科且有删改,不保证真实准确性)

Input

第一行一个整数T(T<=50),代表数据组数在每组数据中:第一行一个整数n(2<=n<=50000)表示军营数目第二行有n-1个整数f【2】f【n】(f【i】<i)第三行有n个整数a【1】a【n】(2<=a【i】<=1e9)第四行有n个整数d【1】~d【n】(1<=d【i】<a【i】)

Output

对于每组数据,输出一行一个整数,表示陆逊攻下所有军营所需使用的最少兵力

Sample Input

1

4

1 1 3

100 50 2 5

2 49 1 1

Sample Output

7

Hint

输入数据量较大,请合理控制输入效率。

样例解释:

首先使用2兵力攻击军营3,并追击至大本营1。然后使用5-1=4兵力攻击军营4。最后使用50-49=1兵力攻击军营2。共使用兵力2+4+1=7。

思路:

​ 一道树形dp的题,比较难。二维数组dp【50005】【3】,其中dp【i】【0】代表以i本营为根节点的子树(攻打i本营无花费)的最小花费,dp【i】【1】代表以i本营为根节点的子树(攻打i本营花费a【i】- d【i】)的最小花费,dp【i】【2】代表以i本营为根节点的子树(攻打i本营花费a【i】)的最小花费。然后找到dp的递推式即可求出

代码:

#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#define ll long long
using namespace std;

int n;
vector <int> zcy[50005];
int two[50005], one[50005];
ll dp[50005][3];    //dp[i][0]代表无花费,dp[i][1]代表仅攻克自己,dp[i][2]代表连带攻克父节点
ll minn[50005];

void dfs(int inx, int father) {
    dp[inx][2] = two[inx];
    dp[inx][1] = one[inx];
    dp[inx][0] = 0;
    for (int i = 0; i < zcy[inx].size(); i++) {
        int ii = zcy[inx][i];
        if (ii == father) continue;
        dfs(ii, inx);
        if (dp[ii][0]) {
            minn[i] = min(dp[ii][0], dp[ii][1]);
            dp[inx][1] += minn[i];
            dp[inx][2] += minn[i];
            dp[inx][0] += minn[i];
        } else {
            minn[i] = dp[ii][1];
            dp[inx][1] += minn[i];
            dp[inx][2] += minn[i];
            dp[inx][0] += minn[i];
        }
    }
    ll k = dp[inx][0];
    if (k == 0) return;
    int suu = 0;
    for (int i = 0; i < zcy[inx].size(); i++) {
        int ii = zcy[inx][i];
        if (ii == father) continue;
        if (!suu) {
            dp[inx][0] = k - minn[i] + dp[ii][2];
        } else {
            dp[inx][0] = min(dp[inx][0], k - minn[i] + dp[ii][2]);
        }
        suu++;
    }
}

int main() {
    int T, a;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++) {
            zcy[i].clear();
        }
        for (int i = 2; i <= n; i++) {
            scanf("%d", &a);
            zcy[i].push_back(a);
            zcy[a].push_back(i);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &two[i]);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &one[i]);
            one[i] = two[i] - one[i];
        }
        dfs(1, 0);
        ll ans;
        if(dp[1][0]) {
            ans = min(dp[1][0], dp[1][1]);
        } else {
            ans = dp[1][1];
        }
        printf("%lld\n", ans);
    }
    return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/84962335