【题目链接】 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);
}
}
}
}