Description
一棵n个节点的树,树上有k个宝石,编号1~k,现在从起点s放一条电子狗,电子狗在每个节点往各邻接点走的概率相同,问电子狗按编号顺序拿完所有宝石的期望步数
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n表示点数,之后n-1行每行两个整数u和v表示u和v在树上有一条边,之后输入一整数q表示查询数,最后q行每行首先输入一个整数k表示宝石数量,然后输入一整数s表示电子狗起点,最后k个整数表示编号从1~k的宝石在树上的位置(T<=10,2<=n<=50000,q<=100,0<=k<=500)
Output
对于每次查询,输出电子狗按顺序拿完所有宝石的期望步数,相邻两组用例的输出用一空行隔开
Sample Input
2
3
1 0
1 2
2
1 0 1
2 0 2 1
4
0 1
2 0
3 0
1
3 0 1 0 1
Sample Output
1.0000
5.0000
11.0000
Solution
关键在于如何求从u点走到v点的期望,考虑到树的性质,不妨取0点为根节点,如果能够求出0点到i点的期望步数down[i]以及i点到0点的期望步数up[i],令w=lca(u,v),那么从u点到v点的期望步数就是up[u]-up[w]+down[v]-down[w],所以问题转化为求up数组和down数组,这两个数组显然是可以通过树形dp去解决,关键在于如何转移,也就是说如何求出i点到父亲fa的期望步数u[i]以及父亲fa到i点的期望步数d[i],下面来解决这个问题,令son[i]为i节点的儿子节点数量,size[i]为以i节点为根节点的子树中节点数量
一.首先求u数组,u[i]表示i点到父亲节点fa的期望步数,i到fa的方法有两种,第一种是直接往上走到fa,第二种是先到i的某个儿子v,然后再从v到i到fa,故有
1.若i点是叶子节点,那么son[i]=0,u[i]=1
2.若i不是叶子节点,由于以i为节点的子树中每个点的父亲节点唯一,考虑上式右边的累加部分,每个节点在计算过程中作为父亲会贡献一个1,作为儿子会贡献一个1(除i节点本身),故有u[i]=2*size[i]-1
二.然后求d数组,d[i]表示从父亲节点fa到i点的期望步数,fa到i的方法有三种,第一种是直接往下走到i,第二种是走到fa的父亲节点再走回到fa再走到i,第三种是走到i的某个兄弟节点v然后从v走到fa再走到i,故有
考虑从0到i点的序列0->v1->v2->…->vm->i,根据上式有
d[i]=d[v1]+2size[v1]-2size[2]+…+2size[vm]-2size[i]=d[v1]+2size[v1]-2size[i]
而求0到其儿子节点v的期望步数有下式
故有d[i]=2n-2size[i]-1
根据u[i]=2size[i]-1和d[i]=2n-2size[i]-1,首先一遍dfs求出size[i],之后进行一遍树形dp即可求得up数组和down数组,用在线倍增法预处理,对于每两个点u和v,O(logn)查询其lca,之后根据up值和down值O(1)求出从u到v的期望步数,总复杂度O(nlogn+qklogn)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
#define maxn 55555
vector<int>g[maxn];
int T,n,q,p[maxn][16],deep[maxn],size[maxn];
ll up[maxn],down[maxn];
void init(int u,int fa)
{
size[u]=1;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa)continue;
deep[v]=deep[u]+1;
p[v][0]=u;
init(v,u);
size[u]+=size[v];
}
}
void lca_init(int n,int root)
{
deep[0]=1;
memset(p,-1,sizeof(p));
init(0,0);
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(~p[i][j-1])
p[i][j]=p[p[i][j-1]][j-1];
}
int lca(int a,int b)
{
int i,j;
if(deep[a]<deep[b])swap(a,b);
for(i=0;(1<<i)<=deep[a];i++);
i--;
for(j=i;j>=0;j--)
if(deep[a]-(1<<j)>=deep[b])
a=p[a][j];
if(a==b) return a;
for(j=i;j>=0;j--)
{
if(p[a][j]!=-1&&p[a][j]!=p[b][j])
{
a=p[a][j];
b=p[b][j];
}
}
return p[a][0];
}
void dfs(int u,int fa)
{
if(fa!=-1)
{
up[u]=up[fa]+2ll*size[u]-1;
down[u]=down[fa]+2ll*(n-size[u])-1;
}
else up[u]=down[u]=0;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa)continue;
dfs(v,u);
}
}
void Solve()
{
int k,u,v,w;
ll ans=0;
scanf("%d%d",&k,&u);
while(k--)
{
scanf("%d",&v);
w=lca(u,v);
ans+=up[u]-up[w]+down[v]-down[w];
u=v;
}
printf("%I64d.0000\n",ans);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)g[i].clear();
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
lca_init(n,0);
dfs(0,-1);
scanf("%d",&q);
while(q--)Solve();
if(T)printf("\n");
}
return 0;
}