2017.11.3 树上期望DP 解题报告

来源:互联网 发布:linux改用户名命令 编辑:程序博客网 时间:2024/06/07 00:38

题目描述

梦游中的你来到了一棵N个节点的树上. 你一共做了Q个梦, 每个梦需要你从点u走到点v之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏醒. 为了避免精度误差, 你要输出答案模109+7的结果.

输入格式

第一行两个整数分别代表N和Q. 接下来N-1行, 每行两个整数u, v代表树中的一条边. 接下来Q行, 每行两个整数代表询问的u,v.

输出格式

一共Q行, 每行一个整数代表答案.

样例

tree.in
4 2
1 2
2 3
3 4
1 4
3 4
tree.out
9
5

数据范围

对于20%的数据, N <= 10.
对于40%的数据, N <= 1000.
另有20%的数据, 保证给定的树是一条链.
对于100%的数据, N <= 100000, Q <= 100000.

【解题报告】

这个小树什么的是骗你的。
我们定义up[i]为节点i到根节点的期望步数,down[i]为根节点到[i]的期望步数
那么显然uv的期望步数为up[u]up[lcm(u,v)]+down[v]down[lcm(u,v)]
我们接着定义u[i]i节点走到它父亲的期望步数,d[i]为从i的父亲节点走到i的期望步数
我们可以的得到
u[i]=2size[i]1
d[i]=2n2size[i]1
一种证明方式http://blog.csdn.net/V5ZSQ/article/details/52314029
直接DP转移即可

代码如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long long#define N 100010#define mod 1000000007int n,q,cnt=-1,head[N];struct Edge{int to,nxt;}e[N<<1];int size[N];LL down[N],up[N];void adde(int u,int v){    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;    e[++cnt].to=u;e[cnt].nxt=head[v];head[v]=cnt;}namespace LCA{    int dis[N],ff[18][N];    void dfs(int p,int father)     {        dis[p]=dis[father]+1,ff[0][p]=father;        for(int i=head[p];~i;i=e[i].nxt)         {            int v=e[i].to;            if(v^father) dfs(v,p);        }    }    void da()     {        for(int j=1;(1<<j)<=n;++j)        for(int i=1;i<=n;++i)            ff[j][i]=ff[j-1][ff[j-1][i]];    }    int query(int x,int y)     {        if(dis[x]<dis[y]) x^=y^=x^=y;        int t=dis[x]-dis[y];        for(int i=0;i<=17;++i)            if(t&(1<<i)) x=ff[i][x];        if(x==y) return x;        for(int i=17;~i;--i)            if (ff[i][x]^ff[i][y]) x=ff[i][x],y=ff[i][y];        return ff[0][x];    }}namespace DP{    void dfs1(int u,int fa)//size    {        size[u]=1;        for(int i=head[u];~i;i=e[i].nxt)        {            int v=e[i].to;            if(v==fa) continue;            dfs1(v,u);            size[u]+=size[v];        }    }    void dfs2(int u,int fa)//down&up(pre_sum!!!)    {        for(int i=head[u];~i;i=e[i].nxt)        {            int v=e[i].to;            if(v==fa) continue;            up[v]=2*size[v]-1+up[u];            down[v]=2*n-2*size[v]-1+down[u];            dfs2(v,u);        }    }}using namespace LCA;using namespace DP;int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    memset(head,-1,sizeof(head));    scanf("%d%d",&n,&q);    for(int i=1,u,v;i<n;++i)    {        scanf("%d%d",&u,&v);        adde(u,v);    }    dfs(1,1);    dfs1(1,1);    up[1]=0;down[1]=0;    dfs2(1,1);    da();//  for(int i=1;i<=n;++i) printf("%d %d\n",up[i],down[i]);    for(int i=1,u,v;i<=q;++i)    {        scanf("%d%d",&u,&v);//      printf("%d %d ",u,v);         int tmp=query(u,v);//printf(" %d ",tmp);//      printf("%d ",tmp);        long long ans=up[u]+down[v]-up[tmp]-down[tmp];        printf("%lld\n",ans%mod);    }    return 0;}/*4 21 22 33 41 43 4*/
原创粉丝点击