题意:链接
方法:树形期望DP+基环树
解析:
首先先看前50%的数据
是一棵树
那么我们可以搞树形DP
然后设几个正常的数组
sum[i]代表i走i的子节点的期望的和。
down[i]代表从底下走到i的期望。
size[i]代表i的儿子个数
up[i]代表从i往上走的期望
然后就可以推式子了
显而易见地可以推出来up的式子
然后有一些奇怪的关于根节点的特判,注意一下就OK了。
然后后50%
我们发现它是一个基环树?
那么首先可以乱搞出来环上的点,然后记录一下这个环上的点的连接方式,求一下相邻两点的距离什么的。
之后呢?
对于每一个环上的点,扫一遍以它为根,不走环的树形DP,同前50%,能扫出其对应子树的那些值。
之后就是更新up了
对于环上的点,我们可以是逆时针走可以是顺时针走,所以先求逆时针,后求顺时针,除个2就行。
但是怎么求呢?
来枚举这个过程。
先从环上一个点,朝逆时针方向走。
找到一个点后,用那个点的down值以及二者的距离更新我们选取的点的up值。
然后别忘了算算一下概率
不妨假设顺序为1 2 3
那我们枚举到1时
走第二个节点的概率是1size[1]+1
从第二个走向第三个的概率也有,所以需要乘一下。
之后就弄完了~不过还是有很多特判
尤其是我的对于前后50%的第二个搜索不一样。。。
也懒得归一了=-=
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std;
int n,m,cnt,tot;
struct node
{
int from,to,val,next;
}edge[N<<1];
int head[N];
int size[N];
int fa[N];
int cntfa[N];
int v[N];
int inc[N];
int cir[N];
int pre[N];
int nex[N];
double sum[N];
double up[N];
double dis[N];
double down[N];
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void edgeadd(int from,int to,int val)
{
edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
edge[cnt].next=head[from];
head[from]=cnt++;
}
int flag;
void getcir(int now,int ff)
{
fa[now]=ff;
v[now]=1;
for(int i=head[now];i!=-1;i=edge[i].next)
{
if(flag)return;
int to=edge[i].to;
if(to==ff)continue;
if(v[to]!=0)
{
cir[++tot]=to;
inc[to]=tot;
cntfa[to]=2;
int tmp=now;
do
{
cir[++tot]=tmp;
cntfa[tmp]=2;
inc[tmp]=tot;
tmp=fa[tmp];
}while(tmp!=to);
flag=1;return ;
}else
{
getcir(to,now);
}
}
}
double map[25][25];
int ccccnt;
void dfscir(int now,int fa)
{
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(!inc[to]||to==fa||ccccnt==tot)continue;
ccccnt++;
pre[to]=now;
nex[now]=to;
dfscir(to,now);
map[inc[to]][inc[now]]=map[inc[now]][inc[to]]=(double)edge[i].val;
break;
}
}
void dfs(int now,int fa)
{
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa||inc[to])continue;
cntfa[to]=1;
dfs(to,now);
dis[to]=edge[i].val;
size[now]++;
sum[now]+=down[to]+edge[i].val;
}
if(size[now]!=0)down[now]=sum[now]/(double)size[now];
}
void dfs3(int now,int fa,int root)
{
if(now!=1)
{
double tmp=sum[fa]-dis[now]+up[fa];
if(size[now]!=0)tmp-=sum[now]/size[now];
if(fa==1)up[now]=tmp/(size[fa]-1);
else up[now]=tmp/size[fa];
up[now]+=dis[now];
}
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa)continue;
dfs3(to,now,root);
}
}
void dfs2(int now,int fa,int root)
{
if(now!=root)
{
double tmp=sum[fa]-dis[now]+up[fa];
if(inc[fa])tmp+=up[fa];
if(size[now]!=0)tmp-=sum[now]/size[now];
if(inc[fa])up[now]=tmp/(size[fa]+1);
else up[now]=tmp/size[fa];
up[now]+=dis[now];
}
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa)continue;
dfs2(to,now,root);
}
}
double ans;
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
edgeadd(x,y,z);
edgeadd(y,x,z);
}
if(m==n-1)
{
dfs(1,-1);
dfs3(1,-1,1);
for(int i=1;i<=n;i++)
{
if(i==1)ans+=(sum[i]+up[i])/(double)(size[i]);
else ans+=(sum[i]+up[i])/(double)(size[i]+1);
}
ans/=(double)n;
printf("%.5lf\n",ans);
}else
{
getcir(1,-1);
dfscir(cir[1],-1);
for(int i=1;i<=n;i++)if(inc[i])dfs(i,-1);
for(int i=1;i<=tot;i++)
{
double k=1;
int now=cir[i];
for(int j=nex[now];j!=now;j=nex[j])
{
if(nex[j]!=now)
{
up[now]+=k*(map[inc[pre[j]]][inc[j]]+down[j]*size[j]/(size[j]+1));
}else
{
up[now]+=k*(map[inc[pre[j]]][inc[j]]+down[j]);
}
k/=(size[j]+1);
}
k=1;
for(int j=pre[now];j!=now;j=pre[j])
{
if(pre[j]!=now)
{
up[now]+=k*(map[inc[nex[j]]][inc[j]]+down[j]*size[j]/(size[j]+1));
}else
{
up[now]+=k*(map[inc[nex[j]]][inc[j]]+down[j]);
}
k/=(size[j]+1);
}
up[now]/=2.0;
}
for(int i=1;i<=tot;i++)
{
int now=cir[i];
for(int j=head[now];j!=-1;j=edge[j].next)
{
int to=edge[j].to;
if(!inc[to])dfs2(to,now,now);
}
}
double ans=0;
for(int i=1;i<=n;i++)
{
if(inc[i])
{
ans+=(up[i]*2+down[i]*size[i])/(double)(2+size[i]);
}else ans+=(up[i]+down[i]*size[i])/(double)(1+size[i]);
}
printf("%.5lf\n",ans/(double)n);
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-13 11:40:53