Vijos P1688 病毒传递

Vijos P1688 病毒传递


题目

背景

突然眼前一阵慌乱,所有的细菌都呈现出惊慌的样子。嘟嘟一问才知道,原来是垂涎已久的小毒物向菌国发起进攻了。
描述

小毒物想要让微生物世界里的所有细菌都感染上病毒。
微生物世界的等级观念很强,除了菌王外,每个细菌均有且只有一个直接的母体,菌王则没有母体。
如果细菌a是细菌b的母体,细菌b是细菌c的母体,则细菌a就是细菌c的母体。绝不会出现这样两个细菌a、b:细菌a是细菌b的母体,细菌b是细菌a的母体。
最开始的时刻为0,小毒物要做的就是用1个单位时间把病毒传递给某一个“病毒源细菌”,然后让细菌们自行传递。在任意一个单位时间中,任何一个已经被感染的细菌,都可以将病毒传递给它的一个直接母体或直接的下一个细菌。
现在,小毒物想知道的是:
1.最少需要多长时间,所有的细菌都会感染上病毒;
2.使感染时间最短时,可供选择的“病毒源细菌”有哪些。
格式

输入格式

第一行一个数N。表示全部的细菌数。并且从1到N编上号,毒王为1。
第二行到第N行(共N-1行),每行一个数。第I行的数表示细菌I的直接母体的编号。
输出格式

第一行一个数。表示最后一个细菌被感染后的最早时间。
第二行若干个数。表示可供选择的“病毒源细菌”的编号。
样例1

样例输入1

8
1
1
1
2
2
3
7

样例输出1

5
1 2 3

限制

各个测试点1s
提示

对于 50% 的测试数据,N<=100
对于 100% 的测试数据,N<=1000


题解

树形DP

先枚举一个起始点,很显然对于该棵病毒传播树中每一个节点的传播病毒的次数都是它儿子的个数

而传播的顺序一定是以它儿子为根的子树传播所需的时间从大到小排序后再加上它在排序中的编号的值(因为父节点一个时间内只能传播一次病毒)

然后就是统计最小的值以及对应的起始点的个数并输出就好了(注意:传给起始点时,额外需要一个时间)


代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#define INF 0x7fffffff
using namespace std;

int n,tot,ans;
int lnk[1005],f[1005],w[1005],a[1005],q[1005];
struct edge
{
    int nxt,y;
} e[2005];

int readln()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*f;
}

int max(int x,int y){return x>y?x:y;}

void add(int x,int y)
{
    tot++;e[tot].nxt=lnk[x];lnk[x]=tot;e[tot].y=y;
}

void qsort(int l,int r)
{
    int i=l,j=r,mid=a[rand()%(r-l+1)+l],t;
    do {
        while (a[i]>mid) i++;
        while (a[j]<mid) j--;
        if (i<=j) {
            t=a[i];a[i]=a[j];a[j]=t;
            i++;j--;
        }
    } while (i<=j);
    if (i<r) qsort(i,r);
    if (l<j) qsort(l,j);
}

void dfs(int x,int fa)
{
    int now=0,y;
    f[x]=0;
    for (int i=lnk[x];i;i=e[i].nxt)
    {
        y=e[i].y;
        if (fa==y) continue;
        dfs(y,x);
    }
    for (int i=lnk[x];i;i=e[i].nxt)
    {
        y=e[i].y;
        if (fa==y) continue;
        a[++now]=f[y];
    }
    if (now!=0) qsort(1,now);
    for (int i=1;i<=now;i++) f[x]=max(f[x],a[i]+i);
}

int main()
{
    n=readln();
    for (int i=2;i<=n;i++)
    {
        int x=readln();
        add(x,i);add(i,x);
    }
    tot=0;ans=INF;
    for (int i=1;i<=n;i++)
    {
        memset(f,0,sizeof(f));
        dfs(i,0);
        if (f[i]<ans) ans=f[i],q[tot=1]=i;
        else if (f[i]==ans) q[++tot]=i;
    }
    printf("%d\n",ans+1);
    for (int i=1;i<=tot;i++) printf("%d ",q[i]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值