JZOJ3501. 【NOIP2013模拟联考15】消息传递

来源:互联网 发布:淘宝生活服务类目 编辑:程序博客网 时间:2024/06/07 00:32

辣鸡题解

题目描述

给出一棵树,可以任意选择一个点作为起始点(消耗1时间)。在1单位时间内,每个上轮已访问过的点可以扩展一个节点,求最少花费时间和最少花费的起点。

20%

DP。设F[i]表示传完i为根的子树所需要的最小时间。
F[i]=max(F[j]+j)json[i]F[j]F[j]

解释一下。
如果i有一些儿子要传,那么肯定先传该从儿子传下去时间大的,否则以后再传所需时间更长。
然后每个点都做一遍就可以20分了。
(25分?玄学)

话说我考试时要是交上去就有25分了

80% & 100%

官方的题解是说把一个点从作为根的父亲上移过去,使其成为新的根。
中途似乎要用到RMQ+二分之类的奇♂妙算法所以果断弃疗

咳咳
讲讲个人的奇♂妙算法。
20%的算法瓶颈在于要把每个点都算一遍,所以时间复杂度是O(N2logn)

G[i]表示假设以i为根的情况下,传遍i的父亲所在子树所需要的最小时间
似乎不易理解实际比题解好理解多了
这里写图片描述

至于G[i]的转移也很显然
这里写图片描述

每次从i的兄弟的F,还有i父亲的G中转移,具体类似F[i]的转移方式。

这样随便水一下能有80分


100分类似80分,只不过有些人工数据十分鬼畜,基本上是一个点连着剩下一片点,这样效率会退化到O(n2logn)

然而解决方法也很简单,只需要从父亲直接推到儿子就行了。
因为一个父亲的儿子中只有一条边会改变,所以把转移的信息记录下来排序,根据每个儿子分别计算答案。
把一个边删掉后,在这条边之前(从大到小排序)的值不变,之后的会-1
问为什么的去看F的转移方程。

这里就不细说了因为太水

code

#include <iostream>#include <cstdio>#define fo(a,b,c) for (a=b; a<=c; a++)#define fd(a,b,c) for (a=b; a>=c; a--)using namespace std;int n,i,j,k,l,m2;int a[200001][3];int ls[200001];int fa[200001];int f[200001];int g[200001];int b[200002][2];int m[200002];int ans[200001];int max(int x,int y) {return (x>y?x:y);}int min(int x,int y) {return (x<y?x:y);}void qsort(int l,int r){    int i,j,k,mid;    i=l;    j=r;    mid=b[(l+r)/2][0];    while (i<=j)    {        while (b[i][0]>mid) i++;        while (b[j][0]<mid) j--;        if (i<=j)        {            k=b[i][0];            b[i][0]=b[j][0];            b[j][0]=k;            k=b[i][1];            b[i][1]=b[j][1];            b[j][1]=k;            i++;            j--;        }    }    if (l<j)    qsort(l,j);    if (i<r)    qsort(i,r);    return;}void dfs(int t){    int i,j;    f[t]=0;    j=0;    for (i=ls[t]; i; i=a[i][2])    dfs(a[i][1]);    for (i=ls[t]; i; i=a[i][2])    b[++j][0]=f[a[i][1]];    qsort(1,j);    fo(i,1,j)    f[t]=max(f[t],b[i][0]+i);    return;}void dfs2(int t){    int i,j;    m2=0;    j=0;    for (i=ls[t]; i; i=a[i][2])    b[++j][0]=f[a[i][1]],b[j][1]=a[i][1];    if (fa[t])    b[++j][0]=g[t],b[j][1]=0;    qsort(1,j);    fd(i,j,1)    m[i]=max(m[i+1],b[i][0]+i);    fo(i,1,j)    {        if (b[i][1])        g[b[i][1]]=max(m2,m[i+1]-1);        m2=max(m2,b[i][0]+i);    }    fo(i,1,j)    m[i]=0;    for (i=ls[t]; i; i=a[i][2])    if (ls[a[i][1]])    dfs2(a[i][1]);    return;}int main(){    freopen("news.in","r",stdin);    freopen("news.out","w",stdout);    scanf("%d",&n);    fo(i,1,n-1)    {        scanf("%d",&fa[i+1]);        a[i][0]=fa[i+1];        a[i][1]=i+1;        a[i][2]=ls[a[i][0]];        ls[a[i][0]]=i;    }    dfs(1);    g[1]=0;    dfs2(1);    j=23333333;    fo(i,1,n)    {        k=0;        if (fa[i])        b[++k][0]=g[i];        for (l=ls[i]; l; l=a[l][2])        b[++k][0]=f[a[l][1]];        qsort(1,k);        fo(l,1,k)        ans[i]=max(ans[i],b[l][0]+l);        ans[i]++;        j=min(j,ans[i]);    }    printf("%d\n",j);    fo(i,1,n)    if (ans[i]==j)    printf("%d ",i);    printf("\n");    fclose(stdin);    fclose(stdout);    return 0;}
原创粉丝点击