zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:树(树形DP+期望)

    题目描述

    梦游中的你来到了一棵$N$个节点的树上。你一共做了$Q$个梦,每个梦需要你从点$u$走到点$v$之后才能苏醒,由于你正在梦游,所以每到一个节点后,你会在它连出去的边中等概率地选择一条走过去,为了确保第二天能够准时到校,你要求出每个梦期望经过多少条边才能苏醒。为了避免精度误差,你要输出答案模${10}^9+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\%$的数据,$Nleqslant 10$。
    对于$40\%$的数据,$Nleqslant 1,000$。
    另有$20\%$的数据,保证给定的树是一条链。
    对于$100\%$的数据,$Nleqslant 100,000,Qleqslant 100,000$。
    如果你求出的答案为$frac{P}{Q}$($P,Q$互质),那么你需要输出$P imes Q^{{10}^9+5}$。


    题解

    从$u$走到$v$一定是从$u$到$fa[u]$,在到$fa[fa[u]],...,$再从$u,v$的$lca$一步一步走到$v$。
    那么如果能够算出从$u$到$fa[u]$的期望步数(设为$f[u]$)和从$fa[u]$到$u$的期望步数(设为$g[u]$)就能够做了。

    那么我们可以列出如下状态转移方程:

      $f[u]=dfrac{1}{deg[u]}+sum limits_{xin child[u]}dfrac{f[x]+f[u]+1}{deg[u]}$

      $g[u]=dfrac{1}{deg[fa[u]]}+dfrac{g[u]+g[fa[u]]+1}{deg[fa[u]]}+sum limits_{xin child[fa[u]]xor x eq u}dfrac{g[u]+f[x]+1}{deg[fa[u]]}$

    发现式子就是在枚举第一步怎么走来列方程。
    化简发现$f[u]$和$g[u]$其实是整数。
    先求$f$,再求$g$,然后求$lca$就可以了。

    三遍$DFS$即可求出答案。

    时间复杂度:$Theta(3 imes N)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[200000];
    int head[100001],cnt;
    int n,q;
    int fa[100001][18],depth[100001];
    long long f[100001],g[100001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void pre_dfs(int x)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!depth[e[i].to])
    		{
    			fa[e[i].to][0]=x;
    			for(int j=1;j<=17;j++)
    				fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
    			depth[e[i].to]=depth[x]+1;
    			pre_dfs(e[i].to);
    		}
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa[x][0])
    			f[x]+=f[e[i].to]+1;
    	f[x]++;
    }
    void pro_dfs(int x)
    {
    	long long res=0;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to==fa[x][0])res+=g[x]+1;
    		else res+=f[e[i].to]+1;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa[x][0])
    		{
    			g[e[i].to]=res-f[e[i].to];
    			pro_dfs(e[i].to);
    		}
    }
    void wzc_dfs(int x)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa[x][0])
    		{
    			f[e[i].to]+=f[x];
    			g[e[i].to]+=g[x];
    			wzc_dfs(e[i].to);
    		}
    }
    int LCA(int x,int y)
    {
    	if(depth[x]>depth[y])swap(x,y);
    	for(int i=17;~i;i--)
    		if(depth[fa[y][i]]>=depth[x])y=fa[y][i];
    	if(x==y)return x;
    	for(int i=17;~i;i--)
    		if(fa[x][i]!=fa[y][i])
    		{
    			x=fa[x][i]; 
    			y=fa[y][i];
    		}
    	return fa[x][0];
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	depth[1]=1;
    	pre_dfs(1);
    	f[1]=0;
    	pro_dfs(1);
    	wzc_dfs(1);
    	while(q--)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		int lca=LCA(x,y);
    		printf("%lld
    ",(f[x]-f[lca]+g[y]-g[lca])%1000000007);
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    返回表对象的方法之一--bulk collect into
    coolite 获取新的页面链接到当前页面指定位置Panel的运用
    oracle 当前年到指定年的年度范围求取
    JAVA WEB 过滤器
    Java复习笔记(二):数据类型以及逻辑结构
    Java复习笔记(一):概念解释和运行步骤
    装饰器理解
    Flask大型项目框架结构理解
    JSP内置对象(一)
    Java Web第一个应用搭建
  • 原文地址:https://www.cnblogs.com/wzc521/p/11464610.html
Copyright © 2011-2022 走看看