欢迎神犇吊打|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-07-13 16:38阅读: 87评论: 0推荐: 0

#P2059. 最大疯子树

#P2059. 最大疯子树

题目描述

给定一棵 n 个结点的树,结点编号为 1~n,i 号结点的权重记为 wi(每个点的权值各不相同)。我们定义一个“疯子树”为:

  • 是一个联通子图。
  • 我们将子图内的点按照权重从小到大排序后序列为 b1,b2,,bm,对于任意的 i(i<m)bibi+1 最短路径(不含 bibi+1 )上的结点的权值都小于 等于 wbi

输出包含结点最多的“疯子树”的结点数。

输入格式

数据有多组 case,文件以 EOF 结束,每组第一行输入一个 n 表示树的节点数;

接下来一行包含 n 个整数,第 i 个数表示 i 号结点的权重 wi;接下行 n-1 行,第 i 行包含 2 个整数 ui, vi,表示 ui和 vi有一条边。

输出格式

对于每组 case 输出一行,包含一个整数,表示包含结点最多的“疯子树” 的结点数。

样例

输入数据 1
5
1 4 2 3 5
1 2
2 3
3 4
4 5
5
2 5 4 1 3
1 2
2 3
3 4
4 5
输出数据 1
4
4

数据规模与约定

对于 10%的数据有所有组的 n 之和 n≤20。

对于 30%的数据有所有组的 n 之和 n≤2000。

对于 100%的数据有所有组的 n 之和 n≤200000,0 < wi≤100000000,n 为正整数。

Solution

很明显,都给你说了是树了,又是这类计数类问题,不用树形 DP 试试怎么行。

先要转化一下题目中“疯子树”的定义,根据题意,其实疯子树就是一个叶子节点到根节点路径上所有点都不严格递减(从下往上),因此可以很轻松想到枚举树根然后对每个树根都进行一次树形 DP 。这样的时间复杂度是 O(n2) 的,所以要考虑怎么样优化。

首先,我们在第一次 DP 的时候,可以维护一个 f[i] 表示以 i 为根的子树的“最大疯子树”的大小,这个 f 很好进行转移,很容易就可以写出其转移方程: f[i]=kson[i],w[i]w[k]f[k] 。既然维护好了一个树根产生的 f ,为什么不在更换树根的时候继续用这个 f 呢?

新定义一个 d[i] 表示整棵树以 i 为根时的“最大疯子树”的大小。那么根据第一遍算出来的 f ,我们可以知道,如果父亲节点的 w 值大于等于当前结点的 w 值,那么就说明这个父亲节点也可以接在 i 上面来构成更大的“疯子树”。因此, d 的转移方程就是: d[i]=f[i]+[w[i]w[fai]]×d[fai] ,因为树形 DP 是由 DFS 实现的,所以 d 的更新放在 dfs 子树之前即可。

这是叫换根法?

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<limits.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
template<typename T> void read(T &k)
{
 	k=0;
	T flag=1;char b=getchar();
	while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
	while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
	k*=flag;
}
const int _SIZE=2e5;
struct EDGE{
	int next,to;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y)
{
	++tot;
	edge[tot].to=y;
	edge[tot].next=head[x];
	head[x]=tot;
}
int n,w[_SIZE+5],f[_SIZE+5],d[_SIZE+5];
void dfs1(int x,int fa)
{
	f[x]=1;
	for (int i=head[x];i;i=edge[i].next)
	{
		int twd=edge[i].to;
		if (twd==fa) continue;
		dfs1(twd,x);
		if (w[twd]>=w[x]) f[x]+=f[twd];
	}
}
void dfs2(int x,int fa)
{
	d[x]=f[x];
	if (w[fa]>=w[x]) d[x]+=d[fa];
	for (int i=head[x];i;i=edge[i].next)
	{
		int twd=edge[i].to;
		if (twd==fa) continue;
		dfs2(twd,x);
	}
}
void solve()
{
	mem(edge,0);mem(f,0);mem(d,0);mem(head,0);tot=0;
	for (int i=1;i<=n;i++) read(w[i]);
	for (int i=1;i<n;i++)
	{
		int t1,t2;
		read(t1),read(t2);
		AddEdge(t1,t2);AddEdge(t2,t1);
	}
	dfs1(1,0);
	dfs2(1,0);
	int ans=INT_MIN;
	for (int i=1;i<=n;i++) ans=max(ans,d[i]);
	printf("%d\n",ans);
}
int main()
{
	while (scanf("%d",&n)!=EOF) solve();
	return 0;
}
posted @   Hanx16Msgr  阅读(87)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起