暂无链接
最大疯子树
【题目描述】
给定一棵n 个结点的树,结点编号为1~n,i 号结点的权重记为wi(每个点
的权值各不相同)。我们定义一个“疯子树”为:
1.是一个联通子图。
2.我们将子图内的点按照权重从小到大排序后序列为b_1,b_2,…,b_m,对于任
意的i(i
【输入格式】
数据有多组case,文件以EOF 结束,每组第一行输入一个n 表示树的节点数;
接下来一行包含n 个整数,第i 个数表示i 号结点的权重wi;接下行n-1 行,第i 行包含2 个整数ui, vi,表示ui 和vi 有一条边。
【输出格式】
对于每组case 输出一行,包含一个整数,表示包含结点最多的“疯子树”
的结点数。
【样例输入】
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
【样例输出】
4
4
【数据范围】
对于10%的数据有所有组的n 之和n≤20。
对于30%的数据有所有组的n 之和n≤2000。
3
对于100%的数据有所有组的n 之和n≤200000,0 < wi≤100000000,n 为正
整数。
题解
我们先看疯子树为一条链的情况:
因为满足最大疯子树的性质,所以在这条链上两端的权值均大于中间的的点的权值,如果我们按照权值和点的编号绘一个图,趋势大概如下:
可以看到大概趋势就是一个凹的形状,那么再考虑复杂点的情况:
我们可以把这个图拆成ABD,ABCD,DBCD这三条链,每条链都应该满足上面的凹形,所以这个图中的权值关系如下图所示:
那么,针对更加复杂的情况,一个疯子树可以想象成一个蜘蛛网,中心为一个权值最小的节点,把整张蜘蛛网向下“拉”。
那么我们考虑如何递推这个疯子树的大小,先考虑从小开始推,但是最后递推的值汇集到了最大的点上,我们并不能通过最大的点上得到的值得到整棵疯子树的大小。
如上图,我们即使得到了4,也并没有什么* 用,因为我们求出的是虚线中“反疯子树”的大小。
那么我们不如反过来推,从大的往小的扩散,这样汇集到最小的点上的时候,就能得出答案。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
struct node{
int w,id;
};
bool operator <(node a,node b){return a.w>b.w;}
int n,val[M];
node pt[M];
vector<int>tree[M];
bool del[M];
void in()
{
int a,b;
for(int i=1;i<=n;++i)
scanf("%d",&pt[i].w),pt[i].id=i;
for(int i=1;i<n;++i)
{
scanf("%d%d",&a,&b);
tree[a].push_back(b);
tree[b].push_back(a);
}
}
void ac()
{
int ans=0;
sort(pt+1,pt+1+n);
for(int i=1;i<=n;++i)
{
del[pt[i].id]=1;
for(int j=tree[pt[i].id].size()-1;j>=0;--j)
{
if(!del[tree[pt[i].id][j]])
val[tree[pt[i].id][j]]+=val[pt[i].id]+1;
}
}
for(int i=1;i<=n;++i)
ans=max(ans,val[i]);
printf("%d\n",ans+1);
memset(val,0,sizeof(val));
memset(pt,0,sizeof(pt));
memset(del,0,sizeof(del));
for(int i=1;i<=n;++i)
tree[i].clear();
}
int main()
{
while(scanf("%d",&n)!=EOF)
in(),ac();
return 0;
}