zoukankan      html  css  js  c++  java
  • Codeforces 2015 ACM Syrian Collegiate Programming Contest K

    题目链接:http://codeforces.com/gym/101086/problem/K
    题意:每个kase有c棵树,每棵树有n个节点。你需要按顺序历遍c棵树。历遍任意一棵树都需要1s。当历遍某一棵树的时候,可以选任意节点为根节点(每个节点被选到的概率都相等),如果以某个节点为根节点并且树的最大深度超过K时,就会爆栈,然后必须用额外3s重启程序,再从第一棵树开始历遍。问在没有爆栈的情况下跑完c棵树所用时间的期望。<题解:这道题关键在于理清题目的过程,然后就是一道求树上每个节点最远路径加个连续期望。题目要求跑完c棵树的时间期望,当在跑某棵树爆栈的时候,上面已经说了,程序会重新从第一棵树开始历遍,并且程序再次启动需要3s,也就是说爆栈后时间是重新算的,只不过需要额外的3s来启动程序。那么我们就可以考虑当程序跑完第i棵树的时候,会产生两种结果:一是没有爆栈进入到下一棵树,此时额外耗费的时间是0;二是爆栈了,然后3s后再重新从第一棵树开始跑,时间重新算(别忘了存在3s的启动时间),那么额外耗费的时间就是3s。然后我们设有p的概率是没有爆栈的,那么第i棵树任意选一个节点对跑完这棵树(包括爆栈和不爆栈)的时间期望的贡献就是(0*p+(1-p)*3),也就是每一个点对跑完第i棵树的贡献,那么从第一棵树到第i棵树的总时间为(ans[i-1]+1+(1-p)*3),这是从n个点选一个点的期望。那么我们再考虑平均选多少个点(也就是爆栈多少次)才能通过这棵树。显然,我们设有ok个节点是不会产生爆栈的,那么平均情况就是n/ok。那么ans[i]=(ans[i-1]+1+(1-p)*3)*n/ok;p=ok/n。具体看代码。                                                         
      那么现在就应该考虑求某棵树的ok个不会产生爆栈的节点,也就是要求树上每个节点的最长路径问题。题目中节点数是1e5,也就是说绝对不能暴力瞎搞。那么这就涉及了一个裸的算法,可以O(n)求出来。我们来考虑树上的最长路径,设端点为A,B,也就是从A节点到B节点的距离是树上的最长路径,那么很明显树上任意一个点的最长路径肯定是从该点到A或者B的路径长度。为什么?举个例子,设树上任意一个节点为C(包括A,B),按照上面所说最长路径要么是AC,要么是CB。假如现在有节点D使的CD的长度大于AC(或者CB),那么我们知道树上最长路径是AB,如果CD是以C为起点的最长路径的话,那么AC+CD>AB(或者CD+CD>AB),此时最长路径就应该是AD(或者BD)了,所以是不存在这个情况的,也就是说结论成立。然后我们考虑当判断编号为a的节点的最长路径的时候,就只有两种情况了,一是从父节点过来的最长路径,二是节点a向下走子节点的最长路径。具体实现看代码。

    代码:

    /*===============================================================
    *  Filename:k.cpp
    *  Author:zhuyutian
    *  Date:2016年09月16日
    ================================================================*/
    #include <bits/stdc++.h>
    using namespace std;
     
    typedef long long ll;
    typedef unsigned long long ull;
    const int N=100005;
    vector<int>g[N];
    int mxd[N][2];
    int dep[N],low[N];
    int T,c,k,n;
    int cnt;
    
    void init()
    {
    	memset(dep,0,sizeof(dep));
    	memset(low,0,sizeof(low));
    	memset(mxd,0,sizeof(mxd));
    	for(int i=1;i<=n;i++)
    		g[i].clear();
    }
    
    void dfs1(int u,int f)
    {
    	low[u]=dep[u];
    	for(int i=0;i<g[u].size();i++)
    	{
    		int v=g[u][i];
    		if(v==f) continue;
    		dep[v]=dep[u]+1;
    		dfs1(v,u);
    		low[u]=max(low[v],low[u]);
    		if(low[v]>mxd[u][0])
    		{
    			mxd[u][1]=mxd[u][0];
    			mxd[u][0]=low[v];
    		}
    		else if(low[v]>mxd[u][1])
    			mxd[u][1]=low[v];
    	}
    }
    
    void dfs2(int u,int f,int up)
    {
    	if(mxd[u][0] - dep[u] <= k && up <= k ) cnt++;//向上走(父节点)和向下走(子节点)
    	for(int i=0;i<g[u].size();i++)
    	{
    		int v=g[u][i];
    		if(v==f)
    			continue;
    		int md=mxd[u][0];
    		if(md==mxd[v][0]) md=mxd[u][1];
    		dfs2(v,u,max(up+1,md-dep[u]+1));
    	}
    }
    
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d%d",&c,&k);
    		double ans=0;
    		for(int i=1;i<=c;i++)
    		{
    			scanf("%d",&n);
    			init();
    			int a;
    			for(int i=2;i<=n;i++)
    			{
    				scanf("%d",&a);
    				g[i].push_back(a);
    				g[a].push_back(i);
    			}
    			cnt=0;
    			dfs1(1,-1);
    			dfs2(1,-1,0);
    			double p=1.0*cnt/n;
    			ans=(ans+1+(1-p)*3.0)/p;//不管爆不爆栈,都需要1s
    		}
    		printf("%.4lf
    ",ans);
    	}
        return 0;
    }
    
    
  • 相关阅读:
    Linux ls -l内容详解
    simple_strtoul
    proc_create的使用方法
    Ubuntu Linux 安装 .7z 解压和压缩文件
    3518调试2
    MTK camera 闪光灯Flashlight驱动调试流程
    samba访问其他服务器文件权限设置
    excel中单列相乘取整
    git fetch
    中文123456789
  • 原文地址:https://www.cnblogs.com/zhuyutian/p/5878907.html
Copyright © 2011-2022 走看看