JZOJ5814. 【NOIP提高A组模拟2018.8.14】 树

Description

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

Input

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

Output

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

Sample Input

4 2
1 2
2 3
3 4
1 4
3 4

Sample Output

9
5

Data Constraint

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

题解

概率题,也就是套路嘛。
f i 表示从i走的 f a i 的期望,
g i 表示从 f a i 走的i的期望。

考虑 f i 的转移,
首先它肯能直接走到它父亲,或者先走到某个儿子,再回到i,在走到它父亲。
f i = ( 1 + x s o n i f x + f i + 1 )
整理一下: f i = x s o n i f x
g i 的转移类似,直接从父亲走过来,或者父亲走到另外的儿子,然后再回到父亲,再走到i,
g i = g f a i + d e g i + x s o n i , x i f x

直接bfs一次就可以求出f跟g了,
求一个前缀和,询问x到y的路径就是
x到lca,再从lca到y。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

const int mo=1000000007;
int n,m,x,y,z,d[N],ans;
int tot,lst[N],to[N*2],nxt[N*2];
int q[N],head,tail,fa[17][N];
int f[N],g[N],dep[N],sum;

void ins(int x,int y)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    lst[x]=tot;
}

ll ksm(ll x,int y) 
{
    ll s=1;
    for(;y;y>>=1,x=x*x%mo)
        if(y&1)s=s*x%mo;
    return s;
}

void add(int& x,int y)
{
    x=x+y>mo?x+y-mo:x+y; 
}

void bfs()
{
    for(head=0,q[tail=1]=1;head<tail;)
    {
        x=q[++head];
        for(int i=lst[x];i;i=nxt[i])
            if(to[i]!=fa[0][x])
            {
                fa[0][to[i]]=x;
                q[++tail]=to[i];
            }
        add(f[x],d[x]);
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int j=16;j>=0;j--)
        if(dep[fa[j][x]]>=dep[y])x=fa[j][x];
    if(x==y)return x;
    for(int j=16;j>=0;j--)
        if(fa[j][x]!=fa[j][y])x=fa[j][x],y=fa[j][y];
    return fa[0][x];
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);

    read(n);read(m);
    for(int i=1;i<n;i++)
        read(x),read(y),d[x]++,d[y]++,ins(x,y),ins(y,x);

    bfs();
    for(int i=n;i;i--)
        for(int j=lst[q[i]];j;j=nxt[j])
            if(to[j]!=fa[0][q[i]])add(f[q[i]],f[to[j]]);

    f[1]=g[1]=0;

    for(int i=1;i<=n;i++)
    {
        dep[q[i]]=dep[fa[0][q[i]]]+1;
        sum=g[q[i]]+d[q[i]];
        for(int j=lst[q[i]];j;j=nxt[j])
            if(to[j]!=fa[0][q[i]])add(sum,f[to[j]]);
        for(int j=lst[q[i]];j;j=nxt[j])
            if(to[j]!=fa[0][q[i]])g[to[j]]=(sum-f[to[j]]+mo)%mo;
    }

    for(int i=1;i<=n;i++)
        add(f[q[i]],f[fa[0][q[i]]]),
        add(g[q[i]],g[fa[0][q[i]]]);

    for(int j=1;j<17;j++)
        for(int i=1;i<=n;i++)
            fa[j][i]=fa[j-1][fa[j-1][i]];

    for(int i=1;i<=m;i++)
    {
        read(x),read(y);
        z=lca(x,y);ans=0;
        add(ans,(f[x]-f[z]+mo)%mo);
        add(ans,(g[y]-g[z]+mo)%mo);
        printf("%d\n",ans);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/81806829