列表

详情


NC221663. 推棋子

描述

牛牛和牛妹又在玩一个游戏。
牛妹和牛牛打赌,如果牛牛输了,今晚牛牛就女装;如果牛妹输了,牛妹今晚就不看牛牛女装。(牛牛:???)

游戏有一张地图,是一棵有n个结点的树。每个结点都有一个分数,编号为i的结点的分数为a_i

在某个结点上面有一颗棋子,牛牛和牛妹沿着边轮流移动棋子,牛牛先,牛妹后。当移动到编号为i的根节点后,移动棋子的那个人的分数就会加上那个结点的分数a_i。棋子不能移动到之前移动过的地方。当棋子无法再移动时则游戏结束。

牛牛想知道当棋子初始位置在哪个结点,(牛牛的分数-牛妹的分数)最大,最大为多少?

输入描述

一共n+1行。
第一行输入一个数n,表示结点个数。
第二行输入n个数a_1,a_2,a_3...a_n表示编号为i的结点上的分数为a_i
第三行到第n+1行每行输入两个数u_iv_i,表示第u_i号结点和第v_i号结点有一条边。

输出描述

共一行,两个数p,q,表示棋子初始位置在p号结点时,(牛牛的分数-牛妹的分数)最大,最大为q,q可能为负数。
如果符合条件的p有多个,则输出编号最小的那个点。

示例1

输入:

5
5 7 2 3 4
1 2
2 3
1 4
1 5

输出:

3 6

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 729ms, 内存消耗: 47352K, 提交时间: 2021-05-25 00:00:48

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>
#define MN 1000000
#define MM 2000000
typedef long long ll;
const ll INF = 1e18;

int n,a[MN+5];
int hd[MN+5],to[MM+5],nxt[MM+5];
ll f[MM+5],ans[MN+5];

void add(int u,int v){
	static int rn = 0;
	to[rn] = v;
	nxt[rn] = hd[u];
	hd[u] = rn++;
}

ll dfs1(int u,int fa){
	ll mx = -INF;
	for(int i=hd[u];~i;i=nxt[i]){
		if(to[i]==fa) continue;
		f[i] = dfs1(to[i],u);
		mx = std::max(mx,f[i]);
	}
	return a[u]-(mx==-INF?0:mx);
}

void dfs2(int u,int fa){
    ll mx[2] = {-INF,-INF};
	for(int i=hd[u];~i;i=nxt[i]){
		if(f[i]>=mx[0]){
			mx[1] = mx[0];
			mx[0] = f[i];
		}else if(f[i]>=mx[1]){
			mx[1] = f[i];
		}
	}
	for(int i=hd[u];~i;i=nxt[i]){
		if(to[i]==fa) continue;
		if(f[i]==mx[0])
			f[i^1] = a[u]-(mx[1]==-INF?0:mx[1]);
		else
			f[i^1] = a[u]-(mx[0]==-INF?0:mx[0]);
		dfs2(to[i],u);
	}
	ans[u] = (mx[0]==-INF?0:mx[0]);
}

int main(){
	memset(hd,0xff,sizeof(hd));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=2;i<=n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	dfs1(1,0);
	dfs2(1,0);
	ll ansv = -1e18;
	int ansi = 0;
	for(int i=1;i<=n;i++){
		if(ans[i]>ansv){
			ansv = ans[i];
			ansi = i;
		}
	}
	printf("%d %lld\n",ansi,ansv);
}

上一题