Codeforces 804D Expected diameter of a tree(树的直径)

F. Expected diameter of a tree
time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Pasha is a good student and one of MoJaK’s best friends. He always have a problem to think about. Today they had a talk about the following problem.

We have a forest (acyclic undirected graph) with n vertices and m edges. There are q queries we should answer. In each query two vertices v and u are given. Let V be the set of vertices in the connected component of the graph that contains v, and U be the set of vertices in the connected component of the graph that contains u. Let’s add an edge between some vertex and some vertex in and compute the value d of the resulting component. If the resulting component is a tree, the value d is the diameter of the component, and it is equal to -1 otherwise. What is the expected value of d, if we choose vertices a and b from the sets uniformly at random?

Can you help Pasha to solve this problem?

The diameter of the component is the maximum distance among some pair of vertices in the component. The distance between two vertices is the minimum number of edges on some path between the two vertices.

Note that queries don’t add edges to the initial forest.

Input
The first line contains three integers n, m and q(1 ≤ n, m, q ≤ 105) — the number of vertices, the number of edges in the graph and the number of queries.

Each of the next m lines contains two integers ui and vi (1 ≤ ui, vi ≤ n), that means there is an edge between vertices ui and vi.

It is guaranteed that the given graph is a forest.

Each of the next q lines contains two integers ui and vi (1 ≤ ui, vi ≤ n) — the vertices given in the i-th query.

Output
For each query print the expected value of d as described in the problem statement.

Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6. Let’s assume that your answer is a, and the jury’s answer is b. The checker program will consider your answer correct, if .

Examples
input
3 1 2
1 3
3 1
2 3
output
-1
2.0000000000
input
5 2 3
2 4
4 3
4 2
4 1
2 5
output
-1
2.6666666667
2.6666666667
Note
In the first example the vertices 1 and 3 are in the same component, so the answer for the first query is -1. For the second query there are two options to add the edge: one option is to add the edge 1 - 2, the other one is 2 - 3. In both ways the resulting diameter is 2, so the answer is 2.

In the second example the answer for the first query is obviously -1. The answer for the second query is the average of three cases: for added edges 1 - 2 or 1 - 3 the diameter is 3, and for added edge 1 - 4 the diameter is 2. Thus, the answer is .

题目大意

  有一个N(1N105)个点的森林,有Q(1Q105)个询问。每次输入两个点,如果这两个点在一棵树上则输出1,否则输出在两棵树之间连一条边后新形成的树的直径的期望。

解题思路

  首先判定输入两个点是否在一棵树上,非常简单,用并查集即可。
  考虑两棵树连接的时候直径的变化,实际上只有两种情况。第一种情况是新的直径是两棵树直径的最大值,第二种情况是两个连接点在原树中的最远距离之和加一。两者最大值即为新的直径。
  那么对于一次查询,我们可以先枚举点少的一棵树上的各个点,然后就可以在另一棵树的每一个点最远距离中二分找到新直径是两者最大值的点数,这样就可以直接算出这些点的贡献,其它的点可以利用前缀和O(1)的得到每个点的贡献。最后除以两棵树点数之积即为答案。
  求树的直径可以用树形DP或从任意一个点DFS,再从最远的点DFS,最远的距离就是直径。树上任意一个点距离最远的点一定是直径的端点之一,所以我们可以从任意点DFS一遍,再从最远点DFS一遍,再从最远点DFS一遍得到所有需要的信息。
  每次查询的时间复杂度为O(log())。对每次查询的结果记录,则最坏情况下每棵树有N个结点,每次查询复杂度为O(NlogN)=O(N12logN)=O(NlogN),再加上预处理的时间复杂度O(NNlogN),所以总时间复杂度为O(QNlogN+NNlogN)

AC代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))
#define sqr(x) ((x)*(x))

const int MAXN=100000+3;
int V, E, Q;
vector<int> G[MAXN];
int par[MAXN], high[MAXN];
int d[MAXN];//直径
vector<int> max_dis[MAXN];//每个点到树上其它点的最大距离
vector<LL> sum[MAXN];//max_dis的前缀和
int the_max, ind, dis[MAXN];
map<pair<int, int>, double> save_ans;//保存答案,遇到相同的询问直接输出

int findfather(int x)
{
    return par[x]=par[x]==x?x:findfather(par[x]);
}

bool unite(int a, int b)
{
    int fa=findfather(a), fb=findfather(b);
    if(fa==fb)
        return false;
    if(high[fa]>high[fb])
        par[fb]=fa;
    else
    {
        par[fa]=fb;
        if(high[fa]==high[fb])
            ++high[fb];
    }
    return true;
}

void init()
{
    for(int i=1;i<=V;++i)
        par[i]=i;
}

void dfs1(int u, int fa, int d)//找最远的点,同时得到每个点到起点低距离
{
    dis[u]=d;
    if(d>the_max)
    {
        the_max=d;
        ind=u;
    }
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(v==fa)
            continue;
        dfs1(v, u, d+1);
    }
}

void dfs2(int u, int fa, int d, int root)//得到每个点到其它点的最大距离
{
    dis[u]=max(dis[u], d);
    max_dis[root].push_back(dis[u]);
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(v==fa)
            continue;
        dfs2(v, u, d+1, root);
    }
}

void pre_work(int u)//对于u所在的树预处理
{
    the_max=0;
    ind=u;
    dfs1(u, -1, 0);
    the_max=0;
    dfs1(ind, -1, 0);
    d[u]=the_max;
    dfs2(ind, -1, 0, u);
    sort(max_dis[u].begin(), max_dis[u].end());
    sum[u].push_back(max_dis[u][0]);
    for(int i=1;i<max_dis[u].size();++i)
        sum[u].push_back(sum[u][i-1]+max_dis[u][i]);
}

int main()
{
    scanf("%d%d%d", &V, &E, &Q);
    init();
    for(int i=0;i<E;++i)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
        unite(u, v);
    }
    for(int i=1;i<=V;++i)
        if(i==findfather(i))
            pre_work(i);
    while(Q--)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        u=findfather(u);
        v=findfather(v);
        if(u==v)
        {
            puts("-1");
            continue;
        }
        if(max_dis[u].size()>max_dis[v].size())
            swap(u, v);
        if(save_ans.find(make_pair(u, v))!=save_ans.end())
        {
            printf("%.10f\n", save_ans[make_pair(u, v)]);
            continue;
        }
        int org_d=max(d[u], d[v]);
        double ans=0;
        for(int i=0;i<max_dis[u].size();++i)
        {
            LL num=upper_bound(max_dis[v].begin(), max_dis[v].end(), org_d-max_dis[u][i]-1)-max_dis[v].begin();
            ans+=num*org_d;
            if(num<max_dis[v].size())
                ans+=(max_dis[v].size()-num)*(max_dis[u][i]+1)+sum[v].back()-(num?sum[v][num-1]:0);
        }
        ans/=max_dis[u].size()*max_dis[v].size();
        save_ans[make_pair(u, v)]=ans;
        printf("%.10f\n", ans);
    }

    return 0;
}
阅读更多
版权声明:转载请注明出处:http://blog.csdn.net/yasola,谢谢 https://blog.csdn.net/Yasola/article/details/78172450
个人分类: 算法 图论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭