码迷,mamicode.com
首页 > 其他好文 > 详细

1775:梦中漫步

时间:2020-02-01 01:05:20      阅读:119      评论:0      收藏:0      [点我收藏+]

标签:str   规模   避免   display   close   限制   lca   做了   bit   

1775:梦中漫步

时间限制: 1000 ms         内存限制: 262144 KB
【题目描述】

梦游中的你来到了一棵N个结点的树上。

你一共做了Q个梦,每个梦需要你从点u走到点v之后才能苏醒。

由于你正在梦游,所以每到一个结点后,你会在它连出去的边中等概率地选择一条边走过去。

为了确保第二天能够准时到校,你要求出每个梦期望经过多少条边才能苏醒。为了避免精度误差,你要输出答案模1e9+7的结果。
【输入】

第一行两个整数分别代表N和Q。

接下来N-1行,每行两个整数u,v代表树中的一条边。

接下来Q行,每行两个整数代表询问的u,v。
【输出】

一共Q行, 每行一个整数代表答案。
【输入样例】

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

【输出样例】

9
5

【提示】

【数据规模】

对于20%的数据,N≤10。

对于40%的数据,N≤1000。

另有20%的数据,保证给定的树是一条链。

对于100%的数据,N≤100000,Q≤100000。

【题解】

类似于囧哥t4的套路。

定义dp[i]为从i节点走到它的父亲节点期望步数,siz[i]为i节点的亲儿子个数+1。

dp[i]=\frac{1}{siz[i]}*1+\sum (1+dp[v]+dp[i])*\frac{1}{siz[i]}。v是i的所有儿子。

移项化简得dp[i]=\sum dp[v]*\+siz[i]。

同理定义sh[i]表示i的祖先走到i的期望步数,求法类似。

最后倍增处理一哈,询问即可nlogn。

代码如下:

技术图片
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int mo=1e9+7;
int n,q,last[N],size,siz[N],dp[N],sh[N],f[N][20],g[N][20],h[N][20],dep[N];
struct pigu
{
    int dao,ne;
}a[N<<1];
inline void lingjiebiao(int x,int y)
{
    a[++size].dao=y;
    a[size].ne=last[x];
    last[x]=size;
}
inline int read()
{
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)) {if(c==-) f=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<3)+(x<<1)+c-0;c=getchar();}
    return x*f;
}
inline void dfs(int now,int fa)
{
    int daan=0;f[now][0]=fa;dep[now]=dep[fa]+1;
    for(int i=1;f[f[now][i-1]][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
    for(int i=last[now];i;i=a[i].ne)
    {
        if(a[i].dao==fa) continue; 
        siz[now]++;siz[a[i].dao]++;
        dfs(a[i].dao,now);
        daan+=dp[a[i].dao];
    }
    if(siz[now]==1) dp[now]=1;
    else dp[now]=siz[now]+daan;
}
inline void dfs2(int now,int fa)
{
    int huo=0;
    for(int i=last[now];i;i=a[i].ne)
    {
        if(a[i].dao==fa) continue;
        huo+=dp[a[i].dao];
    }
    if(fa) huo+=sh[now];
    for(int i=last[now];i;i=a[i].ne)
        if(a[i].dao!=fa)
            sh[a[i].dao]=siz[now]+huo-dp[a[i].dao];
    for(int i=last[now];i;i=a[i].ne)
        if(a[i].dao!=fa)
            dfs2(a[i].dao,now);
}
inline void dfs3(int now)
{
    g[now][0]=dp[now];h[now][0]=sh[now];
    for(int i=1;f[now][i];i++) g[now][i]=(g[now][i-1]+g[f[now][i-1]][i-1])%mo,h[now][i]=(h[now][i-1]+h[f[now][i-1]][i-1])%mo;
    for(int i=last[now];i;i=a[i].ne)
    {
        if(f[now][0]==a[i].dao) continue; 
        dfs3(a[i].dao);
    }
}
inline int get_lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=19;i>=0;i--) 
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
int main()
{
    n=read();q=read();    
    for(int i=1,x,y;i<=n-1;i++)
    {
        x=read();y=read();
        lingjiebiao(x,y);
        lingjiebiao(y,x);
    }
    dfs(1,0);    
    dfs2(1,0);
    dfs3(1);
    for(int i=1,x,y;i<=q;i++)
    {
        int ans=0;
        x=read();y=read();
        int lca=get_lca(x,y);
        for(int i=19;i>=0;i--)
            if(dep[f[x][i]]>=dep[lca])
                ans+=g[x][i],ans%=mo,x=f[x][i];
        for(int i=19;i>=0;i--)
            if(dep[f[y][i]]>=dep[lca])
                ans+=h[y][i],ans%=mo,y=f[y][i];
        cout<<ans<<"\n";
    }
    return 0;
} 
View Code

 

1775:梦中漫步

标签:str   规模   避免   display   close   限制   lca   做了   bit   

原文地址:https://www.cnblogs.com/betablewaloot/p/12247263.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!