【GMOJ3501】消息传递
题目
题目链接:https://gmoj.net/senior/#main/show/3501
H国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果A是B的上级,B是C的上级,那么A就是C的上级。绝对不会出现这样的关系:A是B的上级,B也是A的上级。
最开始的时刻是0,你要做的就是用1单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。
现在,你想知道:
1.到底需要多长时间,消息才能传遍整个H国的所有人?
2.要使消息在传递过程中消耗的时间最短,可供选择的人有那些?
思路
洛谷P2018是这道题的弱化版,复杂度\(O(n^2)\)即可过。
50pts
树形\(dp\)。分别枚举每一个点开始传递时的答案,然后取\(min\)。假设现在从\(i\)开始传递,那么我们设\(i\)为树根,要往叶子节点传递。
在以\(i\)为根的情况下,假设我们处理到以\(x\)为根的子树,设\(f[i]\)表示处理完\(i\)为根的子树的最少时间,那么显然应将\(x\)的儿子的\(f[i]\)从大到小的顺序传递。
那么有
\[f[x]=min_{y\in x's\ son}(f[y]+ord[y])
\]
其中\(ord[y]\)表示\(y\)的排名。
时间复杂度\(O(n^2\log n)\)。
100pts
先求出\(1\)为根时的答案,接下来换根即可。
维护每一个点的前后缀\(max\),从\(x\to y\)时,将\(1\sim ord[y]-1\)的\(min\)和\(ord[y]+1\sim cntson[x]\)的\(min-1\)求\(min\)即可。
时间复杂度\(O(n\log n)\)。
代码
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010;
int n,ans,tot,q[N],f[N],head[N],cnt[N];
vector<int> pos,maxn[N][3];
map<int,int> rk[N];
struct edge
{
int next,to;
}e[N*2];
bool cmp(int x,int y)
{
return f[x]>f[y];
}
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void solve(int x,int fa)
{
cnt[x]=0;
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) q[++cnt[x]]=e[i].to;
sort(q+1,q+1+cnt[x],cmp);
maxn[x][1].clear(); maxn[x][2].clear();
maxn[x][1].push_back(0); maxn[x][2].push_back(0);
for (int i=1;i<=cnt[x];i++)
{
maxn[x][1].push_back(max(maxn[x][1][i-1],f[q[i]]+i));
rk[x][q[i]]=i;
}
for (int i=cnt[x];i>=1;i--)
maxn[x][2].push_back(max(maxn[x][2][cnt[x]-i],f[q[i]]+i));
maxn[x][1].push_back(0); maxn[x][2].push_back(0);
}
void dp(int x,int fa)
{
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) dp(e[i].to,x);
solve(x,fa);
f[x]=maxn[x][1][cnt[x]];
}
void dfs(int x,int fa)
{
if (x!=1)
{
// printf("%d\n",rk[fa][x]);
f[fa]=max(maxn[fa][1][rk[fa][x]-1],maxn[fa][2][cnt[fa]-rk[fa][x]]-1);
solve(x,0);
f[x]=maxn[x][1][cnt[x]];
if (f[x]<ans)
{
pos.clear();
ans=f[x]; pos.push_back(x);
}
else if (f[x]==ans) pos.push_back(x);
}
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,x);
if (x!=1)
{
f[x]=max(maxn[x][1][rk[x][fa]-1],maxn[x][2][cnt[x]-rk[x][fa]]-1);
f[fa]=maxn[fa][1][cnt[fa]];
}
}
int main()
{
freopen("news.in","r",stdin);
freopen("news.out","w",stdout);
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=2,x;i<=n;i++)
{
scanf("%d",&x);
add(x,i); add(i,x);
}
dp(1,0);
ans=f[1]; pos.push_back(1);
dfs(1,0);
sort(pos.begin(),pos.end());
printf("%d\n",ans+1);
for (int i=0;i<pos.size();i++)
printf("%d ",pos[i]);
return 0;
}