您的位置:首页 > 其它

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

2017-12-11 20:41 639 查看
辣鸡题解

题目描述

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

20%

DP。设F[i]表示传完i为根的子树所需要的最小时间。

则F[i]=max(F[j]+j) , j∈son[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: