CF804D Expected diameter of a tree(期望+根号分治)
tag
期望,根号分治。
大致题意:
给你一个森林,每次询问两个点,求把两个点所在联通块连接起来生成的树的直径的期望。
分析:
如果是期望的话,只需要求出所有可能情况下的能生成的直径的和,再除以 \(siz_u\times siz_v\) ,就是期望。所以我们的目标就是怎样求所有情况下的直径的和。
考虑根号分治。我们把森林的按照点的数量分成大于 \(\sqrt n\) 的和小于等于 \(\sqrt n\) 的部分。然后是先考虑有一棵树的大小是小于等于 \(\sqrt n\) 的情况。我们可以枚举这棵树上的每个点,然后计算另外那棵的所有点能做出的贡献,这样枚举点不超过 \(\sqrt n\),所以我们要考虑怎样去快速求解另外那棵树能做出的所有贡献。
然后可以想到,连接两个联通块之后生成的直径有三种情况:一种是原来两个联通块的直径的一个,另外一种是被连接的两个点中形成一条更长的直径。我们现在已经枚举了其中一个点,所以就是剩下的一个点能延伸的最大长度加上枚举的点能延伸的最大长度再加 \(1\) 如果超过了两个联通块原本的最大直径,就说明连到这个点上,答案就是刚刚说的第二种情况否则就一定是第一种情况。也就是说我们一定能把一个联通块分成两部分,一部分是属于第一种情况,另一部分属于第二种情况。而且你可以发现,判断一个点属于那种情况之和它能延伸出的最长长度有关,所以我们可以先预处理出所有点能延伸的最大长度,排序,然后二分,找到一个分界点使得它左边都属于第一种,右边都属于第二种,第一部分的答案贡献好求,就数量乘两个联通块直径的较大值,第二部分的答案可以预处理前缀和,因为贡献可以拆开,变成枚举的点的贡献 \(+1\) 和现在的点的贡献。
然后有一块小于 \(\sqrt n\) 的你就会求了。
现在看都大于 \(\sqrt n\) 的情况。其实这部分可以预处理,就是提前对联通块按大小排序,然后对每个大于 \(\sqrt n\) 的块,枚举每个点,然后对于所有比它大的联通块像上面一样求答案。听起来好暴力,但是考虑到先是最多有 \(n\) 个点会被枚举,枚举另外一个联通块的复杂度是 \(O(\sqrt n)\) 的,然后上面再套个二分,就是 \(O(n\sqrt n \log n)\) 的。(这复杂度其实还是离谱,但是他应该可过)
所以总复杂度是 \(O(n\sqrt n \log n +q\sqrt n \log n)\),你需要控制一下常数什么的。
但是我还没调完((((
明天调好不好
调完了,一个手滑错误调一年
#include <bits/stdc++.h>
using namespace std;
int n,m,Q;
#define int long long
const int maxn=100020;
const int maxm=300020;
int to[maxm],nxt[maxm],head[maxn],num;
void add(int x,int y){num++;to[num]=y;nxt[num]=head[x];head[x]=num;}
int siz[maxn],sz[maxn],len[maxn],slen[maxn],son[maxn],mx[maxn];bool vis[maxn];
vector<int> q[maxn],f[maxn];
map<int,int> ANS[maxn];
int cnt,c[maxn];
void dfs(int p,int fa,int col)
{
siz[p]=1;vis[p]=1;c[p]=col;
q[col].push_back(p);
for(int i=head[p];i;i=nxt[i])
{
if(to[i]==fa) continue;
dfs(to[i],p,col);
siz[p]+=siz[to[i]];
mx[col]=max(mx[col],len[p]+len[to[i]]+1);
if(len[to[i]]>=len[son[p]]) slen[p]=len[p],son[p]=to[i],len[p]=len[to[i]]+1;
else slen[p]=max(slen[p],len[to[i]]+1);
}
}
void dfs2(int p,int fa,int col,int l)
{
vis[p]=1;
if(son[p]) dfs2(son[p],p,col,max(slen[p]+1,l+1));
for(int i=head[p];i;i=nxt[i])
{
if(to[i]==fa||to[i]==son[p]) continue;
dfs2(to[i],p,col,max(l+1,len[p]+1));
}
len[p]=max(len[p],l);
}
int idx[maxn];int B;
bool cmp(int x,int y)
{
return sz[x]<sz[y];
}
bool cmp2(int x,int y)
{
return len[x]<len[y];
}
void solve()
{
for(int i=1;i<=cnt;i++) idx[i]=i;
for(int i=1;i<=cnt;i++)
{
sort(q[i].begin(),q[i].end(),cmp2);
if(q[i].size())
f[i].push_back(len[q[i][0]]);
for(int j=1;j<q[i].size();j++)
f[i].push_back(f[i][j-1]+len[q[i][j]]);
// fprintf(stderr,"%d: %d %d\n",i,sz[i],f[i].size());
// for(auto x:f[i]) fprintf(stderr,"%d ",x);
// fprintf(stderr,"\n");
}
sort(idx+1,idx+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
{
if(sz[idx[i]]<=B) continue;
for(auto x:q[idx[i]])
{
for(int j=1+i;j<=cnt;j++)
{
int MX=max(mx[idx[i]],mx[idx[j]]);
int l=1,r=sz[idx[j]],ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(len[q[idx[j]][mid-1]]+len[x]+1<MX)
ans=mid, l=mid+1;
else r=mid-1;
}
if(ans>0) ANS[idx[i]][idx[j]]=ANS[idx[j]][idx[i]]+=ans*MX+f[idx[j]][sz[idx[j]]-1]-f[idx[j]][ans-1]+(sz[idx[j]]-ans)*(len[x]+1);
else ANS[idx[i]][idx[j]]=ANS[idx[j]][idx[i]]+=f[idx[j]][sz[idx[j]]-1]+sz[idx[j]]*(len[x]+1);
}
}
}
}
int getans(int u,int v)
{
int ret=0;
for(auto x:q[c[u]])
{
int MX=max(mx[c[u]],mx[c[v]]);
int l=1,r=sz[c[v]],ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(len[q[c[v]][mid-1]]+len[x]+1<MX)
ans=mid, l=mid+1;
else r=mid-1;
}
if(ans>0) ret+=ans*MX+f[c[v]][sz[c[v]]-1]-f[c[v]][ans-1]+(sz[c[v]]-ans)*(len[x]+1);
else ret+=f[c[v]][sz[c[v]]-1]+sz[c[v]]*(len[x]+1);
}
return ret;
}
signed main()
{
//freopen("p.in","r",stdin);
//freopen("p.out","w",stdout);
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%lld%lld",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++)
{
if(!vis[i]) dfs(i,0,++cnt),sz[cnt]=siz[i];
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
if(!vis[i]) dfs2(i,0,c[i],0);
B=sqrt(n); solve();
while(Q--)
{
int u,v;scanf("%lld%lld",&u,&v);
if(c[u]==c[v]) {printf("-1\n");continue;}
if(sz[c[u]]>B&&sz[c[v]]>B)
{
printf("%.6lf\n",1.0*ANS[c[u]][c[v]]/((double)sz[c[u]]*sz[c[v]]));
continue;
}
if(sz[c[u]]>sz[c[v]]) swap(u,v);
printf("%.6lf\n",1.0*getans(u,v)/((double)sz[c[u]]*sz[c[v]]));
}
}