jzoj4225. 【五校联考3day1】宝藏

在这里插入图片描述

solution

  • 很明显,这道题就是要求,两个点之间的期望步数,那么我们知道,对于两个点x,y肯定是x走到lca再走下去到y
  • 我们设f,g分别为由x走到他父亲的期望步数,和由父亲走来的期望步数
  • f[x]=1/deg[x]+ ∑ u ∈ s o n [ x ] \sum_{u \in son[x]} uson[x](f[u]+f[x]+1)/deg[x]
  • g[x]=1/deg[fa[x]]+1/deg[fa[x]]*(g[x]+g[fa[x]]+1)+ ∑ u ∈ s o n [ x ] a n d u ! = x \sum_{u \in son[x]and u!=x} uson[x]andu!=x(f[u]+1+g[x])/deg[fa[x]]
  • 简单解释下,g最后的那个转移,就是走到fa的一个非x的子节点,然后走上来,再走到x,总之都是在枚举第一步怎么走。
  • 然后化简一下,f[x]两边同时乘上deg[x],然后右边的 ∑ \sum f[u]=(deg[x]-1)*f[u]
  • g同理
  • 然后lca搞一下就没了。
  • Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (int (i)=(b);(i)>=(a);(i)--)
#define Fo(i,x) for (int i=head[x];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=50000+55;
int head[N],to[N*2],next[N*2],d[N],h[N][21],tot,v,n,q,m;
int  f[N],g[N],ans,deg[N]; 
void R(int &n)
{
	int t=0,p=1;char ch;
	for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())
		if(ch=='-') p=-1;
	for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
	n=t*p;
}
void add(int x,int y){
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
}
void dfsf(int x){
	Fo(i,x){
		int v=to[i];
		if (v==h[x][0]) continue;
		h[v][0]=x;d[v]=d[x]+1;
		dfsf(v);
		f[x]+=f[v];
	}
	f[x]=(f[x]+deg[x]);
}
void dfsg(int x){
	int sum=0;
	Fo(i,x) {
		int v=to[i];
		if (v!=h[x][0])
		sum+=f[v];
	}
	Fo(i,x){
		int v=to[i];
		if (v==h[x][0]) continue;
		g[v]=g[x]+deg[x]+sum-f[v];
		dfsg(v);
	}
}
void dfs(int x){
	Fo(i,x){
		int v=to[i];
		if (v==h[x][0]) continue;
		f[v]+=f[x];
		g[v]+=g[x];
		dfs(v);
	}
}
int lca(int x,int y){
	if (d[x]<d[y]) swap(x,y);
	fd(k,20,0)
		if (d[h[x][k]]>d[y]) x=h[x][k];
	if (d[x]!=d[y]) x=h[x][0];
	fd(k,20,0)
		if (h[x][k]!=h[y][k])  x=h[x][k],y=h[y][k];
	if (x==y) return x;
	return h[x][0];
}
int main(){
	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	int T;
	R(T);
	while (T--){
		tot=0;
		memset(head,0,sizeof(head));
		memset(deg,0,sizeof(deg));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		R(n);
		int x,y,k;
		fo(i,1,n-1){
		R(x);R(y);
		x++;y++;
		add(x,y);add(y,x);deg[x]++;deg[y]++;
		}
		h[x][0]=1;
		dfsf(1);
		f[1]=0;
		dfsg(1);
		dfs(1);
		fo(j,1,20)	
			fo(i,1,n)
				h[i][j]=h[h[i][j-1]][j-1];
		int p;
		int a[501];
		R(q);
		while (q--)
		{
			scanf("%d",&p);
			fo(i,0,p) scanf("%d",&a[i]),a[i]++;
			ans=0;
			for (int i=1;i<=p;i++)
			{
				int l=lca(a[i-1],a[i]);
				ans+=f[a[i-1]]-f[l]+g[a[i]]-g[l];
			}
			printf("%d.0000\n",ans);
		} 
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值