首页 > 代码库 > Codeforces 235D Graph Game

Codeforces 235D Graph Game

题目大意

  给定一个环套树,类似于点分的过程,这样定义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 }
View Code

 

Codeforces 235D Graph Game