HDU 6662 Acesrc and Travel (换根dp)

Problem Description
Acesrc is a famous tourist at Nanjing University second to none. During this summer holiday, he, along with Zhang and Liu, is going to travel to Hong Kong. There are n spots in Hong Kong, and n1 bidirectional sightseeing bus routes connecting these spots. They decide to visit some spots by bus.

However, Zhang and Liu have different preferences for these spots. They respectively set a satisfactory value for each spot. If they visit the ith spot, Zhang will obtain satisfactory value ai, and Liu will obtain bi. Starting with Zhang, they alternately decide the next spot to visit for the sake of fairness. There must be a bus route between the current spot and the next spot to visit. Moreover, they would never like to visit a spot twice. If anyone can't find such a next spot to visit, they have no choice but to end this travel.

Zhang and Liu are both super smart competitive programmers. Either want to maximize the difference between his total satisfactory value and the other's. Now Acesrc wonders, if they both choose optimally, what is the difference between total satisfactory values of Zhang and Liu?
 

 

Input
The first line of input consists of a single integer T (1T30), denoting the number of test cases.

For each test case, the first line contains a single integer n (1n105), denoting the number of spots. Each of the next two lines contains n integers, a1,a2,,anand b1,b2,,bn (0ai,bi109), denoting the 
satisfactory value of Zhang and Liu for every spot, respectively. Each of the last n1 lines contains two integers x,y (1x,yn,xy), denoting a bus route between the xth spot and the yth spot. It is reachable from any spot to any other spot through these bus routes.

It is guaranteed that the sum of n does not exceed 5.01×105.
 

 

Output
For each test case, print a single integer in one line, the difference of total satisfactory values if they both choose optimally.
 

 

Sample Input
1 3 1 1 1 0 2 3 1 2 1 3
 

 

Sample Output
-1
 
题意:
给定一棵树,每个节点上对应两个权值,分别是两个人的满意度,他们两个都想让自己的满意度减去另外一个人的满意度之和尽可能大,求最终得到的值.
注意,这两个人交替选择方向,并且不能走回头路,由第一个人选择起始点.
(题意太复杂,说得不好请见谅)
思路:
树形dp,求出以1号节点为起始点时,每个点由第一个人和第二个人选择而来得到的最大值和次大值.
然后换根即可.
其中有很多地方需要注意,如如果一个点只有一个儿子结点或者本身是叶子结点时,需要特殊处理一下,因为在换根途中,切断其与父亲结点的关系之后,其最大值和最小值可能存在问题.
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>

#define fuck(x) cerr<<#x<<" = "<<x<<endl;
#define debug(a, x) cerr<<#a<<"["<<x<<"] = "<<a[x]<<endl;
#define lson l,mid,ls
#define rson mid+1,r,rs
#define ls (rt<<1)
#define rs ((rt<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int loveisblue = 486;
const int maxn = 100086;
const int maxm = 200086;
const ll Inf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-6;
const double pi = acos(-1);

int Head[maxn], cnt;
struct edge {
    int Next, v;
} e[maxm];

void add_edge(int u, int v) {
    e[cnt].Next = Head[u];
    e[cnt].v = v;
    Head[u] = cnt++;
}

ll a[maxn];

ll dp1[2][maxn];//0由第一个人选择而来,1第二个选择而来
ll dp2[2][maxn];//
bool vis[maxn];

void dfs(int u, int fa) {
    bool flag = true;
    for (int k = Head[u]; k != -1; k = e[k].Next) {
        if (e[k].v == fa) {
            continue;
        }
        dfs(e[k].v, u);
        flag = false;

        ll tmp1 = dp1[1][e[k].v];
        ll tmp0 = dp1[0][e[k].v];

        //当前由第一个人选择而来的,那下一个一定由第二个人选择而来
        if (tmp1 < dp2[0][u]) { swap(tmp1, dp2[0][u]); }
        if (dp2[0][u] < dp1[0][u]) { swap(dp2[0][u], dp1[0][u]); }

        if (tmp0 > dp2[1][u]) { swap(tmp0, dp2[1][u]); }
        if (dp2[1][u] > dp1[1][u]) { swap(dp2[1][u], dp1[1][u]); }
    }

    vis[u] = flag;
    //叶子节点的特殊处理
    if (flag) {
        dp1[0][u] = 0;
        dp1[1][u] = 0;
    }
    if (dp1[0][u] != Inf) dp1[0][u] += a[u];
    if (dp1[1][u] != -Inf) dp1[1][u] += a[u];
    if (dp2[0][u] != Inf) dp2[0][u] += a[u];
    if (dp2[1][u] != -Inf) dp2[1][u] += a[u];
}

ll ans = -Inf;

void dfs1(int u, int fa) {

    ll tmp0 = dp1[0][fa];
    ll tmp1 = dp1[1][fa];


    //如果父亲节点的最值是由u转移来的,那么就要利用次值换根
    if (tmp0 == dp1[1][u] + a[fa]) {
        tmp0 = dp2[0][fa];
    }
    if (tmp1 == dp1[0][u] + a[fa]) {
        tmp1 = dp2[1][fa];
    }

    //如果fa是只有u这一个儿子,并且fa==1时,才会出现这种情况
    if (tmp1 == -Inf) {
        tmp1 = a[fa];
    }
    if (tmp0 == Inf) {
        tmp0 = a[fa];
    }
    tmp0 += a[u];
    tmp1 += a[u];


    //u是叶子节点,直接特判
    if (vis[u]) {
        ans = max(ans, tmp1);
    }
    //此段代码和dfs中的完全相同
    if (tmp1 < dp2[0][u]) { swap(tmp1, dp2[0][u]); }
    if (dp2[0][u] < dp1[0][u]) { swap(dp2[0][u], dp1[0][u]); }

    if (tmp0 > dp2[1][u]) { swap(tmp0, dp2[1][u]); }
    if (dp2[1][u] > dp1[1][u]) { swap(dp2[1][u], dp1[1][u]); }

    ans = max(ans, dp1[0][u]);

    for (int k = Head[u]; k != -1; k = e[k].Next) {
        if (e[k].v != fa)dfs1(e[k].v, u);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int T;
    scanf("%d", &T);
    while (T--) {
        int n;
        ans = -Inf;
        cnt = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            Head[i] = -1;
            scanf("%lld", &a[i]);
            dp1[0][i] = dp2[0][i] = Inf;
            dp1[1][i] = dp2[1][i] = -Inf;

        }
        for (int i = 1; i <= n; i++) {
            ll x;
            scanf("%lld", &x);
            a[i] -= x;
        }
        for (int i = 1; i < n; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            add_edge(x, y);
            add_edge(y, x);
        }
        dfs(1, 0);
        ans = dp1[0][1];
        for (int k = Head[1]; k != -1; k = e[k].Next) {
            dfs1(e[k].v, 1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

对代码有问题可以留言

posted @ 2019-08-15 01:09  断腿三郎  阅读(448)  评论(0编辑  收藏  举报