GDOI【JZOJ4794】富爷说是一棵树
2016-09-25 08:56
375 查看
Description
富爷说来一棵树,于是大头栽了一棵树。树大了,有n个点和n - 1条边,任意两个点都是联通的,点的标号为1 - n。爱树的大头和富爷在树上安居乐业,但大头住在u,而富爷住在v,他们都很不高兴,因为u到v有且只有一条简单路径。当然了,树王富爷找到了解决办法,他打算带着大头再给树建一条边(保证不是自环),而且他们会在n * (n - 1) / 2的方案中随机选择一种。
但,要让富爷和大头开心是有条件的。只有新建边之后,富爷去大头家以及大头去富爷家存在两条路径不会走相同的边时,他们才会呵呵(也就是说 存在一个简单环包含u和v)。
不开心的事情选择忘记。当富爷和大头开心时,你能得到愉快值等于环的大小。所以,你要告诉富爷和大头,当他们开心时(只考虑在环内),他们的期望愉悦值。
Data Constraint
20% n,m <= 200040% n,m <= 10^5 树是随机的
60% n,m <= 10^5 每个点的度数均为2
100% n,m <= 10^5
Solution
这种题一看就知道是用lca搞最近公共祖先。我们对情况分一下类:
1、当询问x,y不是祖先关系时(即x不是y的儿子或y不是x的儿子),我们只需将双方的子树的大小分别乘上对方子树所有点到该子树顶点的距离,相加再除以情况数即可。式子为ans=(size[x]*g[y]+size[y] *g[x])/(size[x] *size[y])(g[i]表示以i为根的子树所有点到该子树顶点i的距离)
2、当询问x,y是祖先关系时,这时我们就要考虑,设k是y的直接儿子且k是x的祖先。这时的连边情况的两个集合显然是(1)除k的子树以外,整棵树所有点和(2)x的子树相连(为什么,你画画图就知道啦)。所以我们仿照情况1已进行计算,稍加改变即可。
代码
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=200005; int first[maxn],last[maxn],next[maxn],f[maxn][20]; ll n,m,i,t,j,k,l,x,y,num,v[maxn],fa[maxn]; ll size[maxn],g[maxn],p[maxn],deep[maxn]; bool bz[maxn]; double len,ans; void lian(int x,int y){ last[++num]=y;next[num]=first[x];first[x]=num; } int lca(int x,int y){ int i,j,t=0,k,l; if (deep[x]<deep[y]) swap(x,y); for (i=log(n)/log(2);i>=0;i--) if (deep[f[x][i]]>=deep[y]) len+=(1<<i),x=f[x][i]; if (x==y) return x; for (i=log(n)/log(2);i>=0;i--) if (f[x][i]!=f[y][i]) len+=2*(1<<i),x=f[x][i],y=f[y][i]; len+=2; return f[x][0]; } int main(){ // freopen("data.in","r",stdin);freopen("data.uot","w",stdout); scanf("%d%d",&n,&m); for (i=1;i<n;i++) scanf("%d%d",&x,&y),lian(x,y),lian(y,x); for (i=1;i<=n;i++) size[i]=1; v[1]=1;i=0;j=1;deep[1]=1;bz[1]=true; while (i<j){ x=v[++i]; for (t=first[x];t;t=next[t]){ if (last[t]==fa[x])continue; bz[last[t]]=true; v[++j]=last[t];fa[last[t]]=x;deep[v[j]]=deep[x]+1; } } while (j) size[fa[v[j]]]+=size[v[j]],f[v[j]][0]=fa[v[j]],g[fa[v[j]]]+=g[v[j]]+size[v[j]],j--; for (i=2;i<=n;i++){ p[v[i]]=g[fa[v[i]]]-g[v[i]]+p[fa[v[i]]]+n-2*size[v[i]]; } for (j=1;j<=log(n)/log(2);j++) for (i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; for (i=1;i<=m;i++){ scanf("%d%d",&x,&y);len=0; if (deep[x]<deep[y]) swap(x,y); t=lca(x,y); if (t!=y) ans=len+(g[x]*size[y]+g[y]*size[x])*1.0/(size[x]*size[y])+1; else{ k=x; for (j=log(n)/log(2);j>=0;j--) if (deep[f[k][j]]>deep[y]) k=f[k][j]; t=g[y]+p[y]-g[k]-size[k]; l=n-size[k]; ans=g[x]*l+t*size[x]+(len+1)*(size[x]*l); ans=ans/(size[x]*l*1.0); } printf("%.8lf\n",ans); } }
相关文章推荐
- 谷歌黑科技WaveNet,更先进的语音合成
- 回忆java来时路-第七章 类的初始化
- 1加到n
- 理解 JavaScript 的函数
- HTML基础 form-textarea创建带有水平垂直滚动条的多行文本框并设置其大小
- PHP $_SERVER['PHP_SELF']、$_SERVER['SCRIPT_NAME'] 与 $_SERVER['REQUEST_URI'] 之间的区别
- 【JZOJ4799】【NOIP2016提高A组模拟9.24】我的快乐时代
- IoDH 实现的单例模式
- 学习IO流之字节流和字符流
- HTML基础 table中的tr中的td标签中的valign属性设置文本靠上,中间,靠下
- HTML基础 legend与fiedset配合创建多组单选按钮(漂亮的)
- HTML基础 form-input中的type属性checkbox 显示一个打钩的多选框框
- Swift 提示:Initialization of variable was never used consider replacing with assignment to _ or removing it
- 2039 骑马修栅栏 (欧拉回路)
- iOS 委托与文本输入(内容根据iOS编程编写)
- HTML基础 form-input-radio创建多组并且每组有多个的单选按钮(不漂亮的)
- HTML基础 form-input-radio创建一个单选按钮(不漂亮的)
- 学习IO流之File类和Files类的使用
- 对抗恶意机器人/自动化行为的新思路与方案
- DVD管理!!