BZOJ 2878 [Noi2012]迷失游乐园 树形期望DP+基环树

来源:互联网 发布:win10怎么ping网络 编辑:程序博客网 时间:2024/05/17 06:37

题意:链接

方法:树形期望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 100010using 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);     }}
0 0
原创粉丝点击