[UESTC SC T1] 最大疯子树

题面:

 

题目描述比较清楚

首先简化情况,考虑仅仅为一条链的情况
那么满足条件的链一定是这样的:



那么这条链的两端一定是大于等于中间的
就形成一个“沟”
接下来我们假设再有一条满足条件的链经过其中一点

那么这样一个东西就可以用一个三维的“坑”或者是“盆”来形象的描述
那么这道题就是从一棵树上选择一个子图,使得这个子图满足两个点之间的路径上的点都小于这两点。

那么这个“盆”怎么去维护呢
有位大佬用   换根法  将这道题O(n)A了

 

 

接下来说说我的做法

对于一个“盆”的结构,我可以直接由“盆”边向“盆”心维护
即从大到小的对于每个节点,维护其所有比他小的能够一次到达的节点。
这样我便能够保证“盆”心能够被所有比它大的“盆”边去维护。
因为是从大到小,所以每个能够作为“盆”边的点或其子节点一定会一次更新到一个“盆”心

    ans[v]+=ans[x]+1
其中v节点的值小于x节点,ans[x]表示以x节点为“盆”心但不包括x节点本身所能形成的最大的满足条件的联通快上的节点的个数

那么最后再枚举一遍所有的点找到ans[x]最大的点的值,再+1进行输出就行了。

就是这样一个过程:

for(int i=1;i<=n;i++)
{
    int x=sa[i].num;
    for(int j=0;j<a[x].size();j++)
	if(del[x]>=del[a[x][j]])
	    ans[a[x][j]]+=(ans[x]+1);
}

其中sa是用来根据点权进行从大到小排序后的数组

想通了之后,便是全代码了:

#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;
vector<int>a[MAXN]; 
struct Nod
{
    int num,val;
}sa[MAXN];
bool operator<(Nod a,Nod b)
{
    return a.val>b.val;
}
int del[MAXN];
int ans[MAXN];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int x,y;
        memset(ans,0,sizeof(ans));
	memset(del,0,sizeof(del));
	for(int i=1;i<=n;i++)
	    a[i].clear();		
        for(int i=1;i<=n;i++)
	{
	    scanf("%d",&sa[i].val);
	    sa[i].num=i;
	    del[i]=sa[i].val;
	}
	sort(sa+1,sa+n+1);
	for(int i=1;i<n;i++)
	{
	    scanf("%d%d",&x,&y);
	    a[x].push_back(y);
	    a[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	{
	    int x=sa[i].num;
	    for(int j=0;j<a[x].size();j++)
		if(del[x]>=del[a[x][j]])
		    ans[a[x][j]]+=(ans[x]+1);
	}
	int anss=0;
	for(int i=1;i<=n;i++)
	    anss=max(anss,ans[i]+1);
	printf("%d\n",anss);
    }
    return 0;
}

 

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值