题目大意
给定一个环套树,类似于点分的过程,这样定义cost:
solve(联通块)
cost+=联通块大小
如果联通块大小=1,return
选定一个点v,删除v
将剩下几个联通块继续调用solve过程
现在每次选定v的时候都是在联通块中等概率随机选定,求cost的期望值。
题解
这题好神……我膜的丽洁姐的题解膜了好长时间。
首先考虑树的情况。考虑event(u,v)表示存在这样一次事件:u,v在一个联通块中,且选定了u作为本次的重心。这样的话对于event(u,v)对于答案有1的贡献。我们只要求出event(u,v)的概率,再把所有的加起来就是答案了。
对于概率怎么求呢?首先对于一个树来说,这个概率就是u,v的路径上u是第一个被选中删去的点的概率。假如u,v之间的有n个节点(包括uv),这个概率是1/n.那么为了方便下面更一般情况的证明,我们要证的是在一条长度为n的从u到v的路径上第一个删掉的节点是u的事件(下面简称“事件”)的概率为1/n。对于这个我们可以用归纳法证明。
首先对于只有两个点(u,v),结论毫无疑问是正确的。
之后我们证明假如对于一个联通块的所有子图都是成立的,那么对于这个联通块也是成立的。设当前联通块有x个点,(u,v)之间有n个点。考虑当前的点应该选哪个。
1.假设选的是(u,v)之间的点,那么只有当选中u时会发生事件,那么选中的是(u,v)之间的点且发生事件概率为1/x。
2.假设选中的不是(u,v)之间的的点,显然选中的概率为(x-n)/x。选中之后考虑包含(u,v)的子图,根据假设再继续选下去发生事件的概率为1/n,因此选不是(u,v)之间的点且发生事件概率为(x-n)/xn
两者相加,得到1/n。
接下来考虑环套树的情况。首先如果(u,v)之间只有一条路,那么情况和树一样。如果(u,v)之间有两条路,我们可以这样考虑。假设(u,v)之间非环上的点数为X,(u,v)之间一条路要经过Y条环边,另一条要经过Z条环边。event(u,v)发生的概率实际上就是这两条路中u是任意一条路上第一个被删除的结点的概率。根据上面已有的结论,我们再容斥一下就可以轻易得到这个概率为1/(X+Y+1)[是第一条路径上第一个删掉的点的概率]+1/(Y+Z+1)[是第二条路径上第一个删掉的点的概率]-1/(X+Y+Z+1)[同时是两条路径上第一个删掉的点的概率]
丽洁姐这场cf终于做完了……题目真的都很interesting啊……虽然我觉得这题最难
1 #include <algorithm> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cstdio> 5 const int N=6005; 6 using namespace std; 7 int q[N],next[N],vis[N],inl[N],head[N],st[N],top,tt,len,u,v,n,num,last[N]; 8 bool flag; 9 double ans; 10 inline void add(int u,int v) 11 { 12 q[++tt]=v;next[tt]=head[u];head[u]=tt; 13 } 14 void dfslen(int i,int pre) 15 { 16 if(flag)return; 17 vis[i]=1;st[++top]=i; 18 for(int j=head[i];j;j=next[j]) 19 { 20 if(q[j]==pre)continue; 21 if(flag)return; 22 if(vis[q[j]]) 23 { 24 for(;st[top]!=q[j];top--)inl[st[top]]=1,len++; 25 inl[st[top]]=1;len++;flag=1; 26 return; 27 } 28 dfslen(q[j],i); 29 } 30 top--; 31 } 32 void get(double y,double z) 33 { 34 if(z<=1)ans+=1/y; 35 else{ 36 double x=y-z+len; 37 ans-=1/x; 38 if(z>=2)ans+=1/(x-(z-2)); 39 if(z<=len)ans+=1/(x-(len-z)); 40 } 41 } 42 void dfs(int i,int y,int z) 43 { 44 last[i]=num;get(y,z); 45 for(int j=head[i];j;j=next[j]) 46 if(last[q[j]]!=num)dfs(q[j],y+1,z+inl[q[j]]); 47 } 48 int main() 49 { 50 scanf("%d",&n); 51 for(int i=1;i<=n;i++) 52 { 53 scanf("%d%d",&u,&v);u++;v++;add(u,v);add(v,u); 54 } 55 dfslen(1,0); 56 for(int i=1;i<=n;i++) 57 { 58 num++;dfs(i,1,inl[i]); 59 } 60 printf("%.12f",ans); 61 return 0; 62 }