• 【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;
    }
    
  • 相关阅读:
    java实现调用打印机代码
    java合并PDF文件
    关于如何把项目做得更好的一次思考
    web语义化之SEO和ARIA
    快速理解web语义化
    使用HTML5地理位置定位到城市的方法及注意事项
    Plupload上传插件简单整理
    两列布局——左侧宽度固定,右侧宽度自适应的两种方法
    Java并发编程之线程基础
    Spring Boot学习之YAML文件配置
  • 原文地址:https://www.cnblogs.com/stoorz/p/12260531.html
Copyright © 2020-2023  润新知