P3174 [HAOI2009]毛毛虫 题解

听说考前1天发题解会rp++哦


 

题目链接

1.解题意

给你一棵树,求树上最大的“毛毛虫”

其中,毛毛虫的定义为:一条主链+主链所包含的所有节点所连的一条边。

2.找思路

容易看出是树上dp。

树上dp最难的环节是思考dp方程。

从叶子节点开始考虑:

对于1个节点,很明显答案是1。

往上一个节点,如果上面的节点不是分叉节点,此时毛毛虫的形状还是条链。答案+1

接着扩大情况,此时,我们遇到了分叉节点。这个节点除了当前的子树之外还有其他分支,考虑分支的合并。

当考虑到当前有分支的节点合并的问题时,我们必定已经遍历完了子树节点,自然清楚各个子树的最大毛毛虫;于是我们可以去max,加上当前节点的大小1,而其他的子树可以作为毛毛虫的“毛”,每一个其他分支可以对答案造成1的贡献。

综合起来,我们设f[v]为包含v节点的v节点及其子树的最大毛毛虫方案。

f[now]=max(f[v])+1+size[now]-2;其中,v是now节点的子节点,size[now]为当前节点连的边的个数,-2就是减去连向父节点的边和最大的f[v]所在的边。

可能有人觉得此时dp方程已经搞完了吗,只差代码实现。但是我们忽略的一个问题:毛毛虫可能盘曲在子树中;

 

 ——来源于洛谷题解

这种情况下,毛毛虫就只能是由:最大的f[v],次大的f[v],size-3得到。

我们还要统计一个次大值才行。

但是由于转移方程的定义,f[]数组不能表示这种情况,只有表示直着的才能转移。

那么我们只需要在每个结点时统计一下答案即可。

ans=mx0+mx1+1+max(0,size[now]-2);

其中,mx0为最大值,mx1为次大值。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300007
using namespace std;
inline int read()
{
    int ans=0;
    char ch=getchar(),last=' ';
    while(ch>'9'||ch<'0')last=ch,ch=getchar();
    while(ch>='0'&&ch<='9')ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    return last=='-'?-ans:ans;
}
int n,a,ans,hea[N],num,siz[N],u,v,m;
int f[N];
struct edg{
    int nex,to;
}edge[N<<1];
inline void add(int from,int to)
{
    num++;
    edge[num].nex=hea[from];
    edge[num].to=to;
    hea[from]=num;
}
int dfs(int now,int fa)
{
    int mx1=0,mx0=0;
    for(int i=hea[now];i;i=edge[i].nex)
    {
        int v=edge[i].to;
        if(v==fa)continue;
        dfs(v,now);
        f[now]=max(f[now],f[v]);
        if(f[v]>mx0)mx1=mx0,mx0=f[v];
        else if(f[v]>mx1)mx1=f[v];
    }
    f[now]+=1+max(0,siz[now]-2);
    ans=max(ans,mx0+mx1+1+max(0,siz[now]-2));
}
int main(){
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        u=read(),v=read();
        add(u,v);siz[u]++;
        add(v,u);siz[v]++;
    }
    dfs(1,0);
    printf("%d\n",ans);
    return 0;
}

完结撒花

csp2020 rp++。

 

posted @ 2020-11-06 10:50  李白莘莘学子  阅读(153)  评论(0编辑  收藏  举报