[CF796C] Bank Hacking

题目

给定一棵带点权树,选出一个最佳的根节点,使得根节点的点权不变,它的儿子点权加(1),其余点点权加(2),并使最大点权最小,输出这个最小的最大点权。(我爱死洛谷上的这个翻译了!原本老长的题设精简成了这么简单易懂的样子)

解说

一眼看去是不是树形(DP)?太复杂了不想写啊,能写别的是不可能写树形(DP)的,这辈子都不可能写树形(DP)的。这么简单的题意,肯定有别的写法吧?
确实有!
总感觉这个题没有代码不太好说,直接看下面代码的注释吧……

代码

#include<bits/stdc++.h>
using namespace std;
#define v e[j].to//注意这里,下面的v都是e[j].to
const int maxn=300000+3;
int a[maxn];
struct edge{
	int next,to;
}e[2*maxn];
int n,ans,fin,tot,head[maxn];//fin代表最终答案
map<int,int> m;//键为点权,值为编号
//因为权值有负数不能用数组
void Add(int a,int b){
	e[tot].to=b;
	e[tot].next=head[a];
	head[a]=tot;
	tot++;
}
int main(){
	ans=-1000000000;//找最大值
	tot=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		ans=max(ans,a[i]);
		m[a[i]]++;//记录权值出现了几次
	}
	for(int i=1;i<=n-1;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		Add(x,y);
		Add(y,x);
	}
	fin=ans+2;//如果没有更小答案fin就是ans+2
	for(int i=1;i<=n;i++){
		bool judge=0;
		for(int j=head[i];j;j=e[j].next){
			m[a[v]]--;//记录儿子的权值
			if(a[v]==ans) judge=1;//记录儿子里是否有最大值
		}
		if(!m[ans]) fin=ans+1;//这说明所有的最大值都在儿子里,所以fin=ans+1
		if(m[ans]==1&&a[i]==ans){//这说明最大值在根节点
			if(judge) fin=ans+1;//最大值在根节点的情况下儿子里还有最大值,肯定儿子因为加了1较大
			else if(m[ans-1]) fin=ans-1+2;//儿子里面没有最大值了但是儿子之外有权值为ans-1的节点权值+2
                        //所以这里为了看的明白没有直接写ans+1
			else{
				fin=ans;//只有根节点是最大值了,没有别的可以超过
				break;//记得break!!!在这里栽了两次
                                //为什么要break?这已经是最佳答案了,之后再计算会再变回更大的值
			}
		}
		for(int j=head[i];j;j=e[j].next) m[a[v]]++;
	}
	printf("%d",fin);
}

幸甚至哉,歌以咏志。

原文地址:https://www.cnblogs.com/DarthVictor/p/12805274.html