D. Expected diameter of a tree(树形dp+期望)

【题目链接】 http://codeforces.com/contest/804/problem/D

【题目大意】

  给你一个森林,每次询问给出u,v,
  从u所在连通块中随机选出一个点与v所在连通块中随机选出一个点相连,
  问你此时相连出的树的直径期望是多少?(如果本身就在同一个连通块内,则输出-1)

【题解】

  我们利用树形dp记录每个点属于的连通块,
  以及每个点到不同分支最远点的距离,记为mxd[i]
  一遍搜索计算出向下最远,再次搜索的时候得到向上最远即可。
  得到各个分支的最远距离之后,我们将其进行排序,
  通过最远和次远分支计算是否可能成为树的直径,更新所属树的直径的答案diam。
  考虑连接u和v所属的树X和树Y,
  如果连接是点x和点y,那么所做的期望贡献就是max(mxdx+mxdy+1,diamX,diamY),
  考虑枚举每个mxdx和mxdy得到组合来计算期望复杂度过高,我们考虑优化,
  我们发现当mxdx+mxdy<max(diamX,diamY)的时候,期望均为max(diamX,diamY)
  那么对于mxdx,我们在mxdy的有序集合Disy中分治查找max(diamX,diamY)-mxdx的位置,
  就得到对于mxdx来说贡献为max(diamX,diamY)的数量,大于部分则直接求和即可,
  我们预处理每个有序集Dis的后缀和,以减少求和的复杂度。
  计算出期望之后,考虑到可能有多组点计算的是同一对树,因此我们用map对答案进行记忆化。

不明白为什么一定要二分,两指针遍历不是O(n)么,不懂T15

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
#include<map>
using namespace std;
int n,m,q;
int belong[100005];
int head[100005];
vector<int> e[100005];
vector<double> dis[100005];
vector<double> sum[100005];

int maxdis[100005];
int dp[100005][5]; //0:dis1 1:nex1 2:dis2 3:nex2


void dfs1(int u,int id)//calculate belong
{
    int len=e[u].size();
    for(int i=0;i<len;i++)
    {
        int v=e[u][i];
        if(belong[v]==0)
        {
            belong[v]=id;
            dfs1(v,id);
        }
    }
}
void dfs2(int u,int fa)// preserve the max 2 values
{
    int len=e[u].size();
    for(int i=0;i<len;i++)
    {
        int v=e[u][i];
        if(v!=fa)
        {
            dfs2(v,u);
            if(dp[v][0]+1>dp[u][0])
            {
                dp[u][2]=dp[u][0];
                dp[u][3]=dp[u][1];

                dp[u][0]=dp[v][0]+1;
                dp[u][1]=v;
            }
            else if(dp[v][0]+1>dp[u][2])
            {
                dp[u][2]=dp[v][0]+1;
                dp[u][3]=v;
            }
        }
    }
}

void dfs3(int u,int fa)
{
    int len=e[u].size();
    if(fa>0)
    {
        if(dp[fa][1]!=u)
        {
            if(dp[fa][0]+1>dp[u][0])
            {
                dp[u][2]=dp[u][0];
                dp[u][3]=dp[u][1];

                dp[u][0]=dp[fa][0]+1;
                dp[u][1]=fa;
            }
            else if(dp[fa][0]+1>dp[u][2])
            {
                dp[u][2]=dp[fa][0]+1;
                dp[u][3]=fa;
            }
        }
        else if(dp[fa][3]!=u)
        {
            if(dp[fa][2]+1>dp[u][0])
            {
                dp[u][2]=dp[u][0];
                dp[u][3]=dp[u][1];

                dp[u][0]=dp[fa][2]+1;
                dp[u][1]=fa;
            }
            else if(dp[fa][2]+1>dp[u][2])
            {
                dp[u][2]=dp[fa][2]+1;
                dp[u][3]=fa;
            }
        }
    }
    for(int i=0;i<len;i++)
    {
        int v=e[u][i];
        if(v!=fa)
        {
            dfs3(v,u);
        }
    }
}

double cal(int x,int y)
{
    if(dis[x].size()>dis[y].size())
        swap(x,y);
    double ans=0;
    int len1=dis[x].size();
    int len2=dis[y].size();
    double maxn=max(dis[x][len1-1],dis[y][len2-1]);
    for(int i=0;i<len1;i++)
    {
        long long tp=lower_bound(dis[y].begin(),dis[y].end(),maxn-dis[x][i])-dis[y].begin();
        double ttp=sum[y][len2-1];
        if(tp!=0)
            ttp-=sum[y][tp-1];
        ans+=1.0*(maxn*tp+(len2-tp)*(dis[x][i]+1)+ttp);
    }
    return ans/(1.0*len1*len2);
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        map<pair<int,int>, double> mp;
        for(int i=0;i<=100000;i++)
        {
            e[i].clear();
            dis[i].clear();
            sum[i].clear();

            maxdis[i]=0;
            for(int j=0;j<5;j++)
                dp[i][j]=0;
        }
        memset(belong,0,sizeof(belong));
        memset(head,0,sizeof(head));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            e[u].push_back(v);
            e[v].push_back(u);
        }
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(belong[i]==0)
            {
                cnt++;
                belong[i]=cnt;
                head[cnt]=i;
                dfs1(i,cnt);
            }
        }
        for(int i=1;i<=cnt;i++)
        {
            dfs2(head[i],-1);
            dfs3(head[i],-1);
        }
        for(int i=1;i<=n;i++)
        {
            int tid=belong[i];
            dis[tid].push_back((double)dp[i][0]);
        }
        for(int i=1;i<=cnt;i++)
        {
            sort(dis[i].begin(),dis[i].end());
            int tlen=dis[i].size();
            sum[i].push_back(dis[i][0]);
            for(int j=1;j<tlen;j++)
            {
                sum[i].push_back(sum[i][j-1]+dis[i][j]);
            }
        }
        while(q--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int id1=belong[u];
            int id2=belong[v];
            if(id1==id2)
            {
                printf("-1\n");
            }
            else
            {
                if(id1>id2)
                    swap(id1,id2);
                if(mp.count(make_pair(id1,id2))==1)
                {
                    printf("%.10f\n",mp[make_pair(id1,id2)]);
                    //cout<<"!!!"<<endl;
                    continue;
                }
                /*int len1=dis[id1].size();
                int len2=dis[id2].size();
                double maxn=max(dis[id1][len1-1],dis[id2][len2-1]);
                double ans=0;
                int cur=len2-1;

                double lsum=0;
                for(int i=0;i<len1;i++)
                {
                    while(cur>=0 && (dis[id1][i]+dis[id2][cur]+1)>=maxn)
                    {
                        lsum+=dis[id2][cur];
                        cur--;
                    }
                    ans+=1.0*((dis[id1][i]+1)*(len2-1-cur)+maxn*(cur+1)+lsum)/(1.0*len1*len2);
                }
                */
                double ans=cal(id1,id2);

                mp[make_pair(id1,id2)]=ans;
                printf("%.10f\n",ans);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/84638301