Codeforces Round #728 (Div. 2) D. Tree Array(期望+枚举)

LINK

考虑枚举第一个被染色的点 r o o t root root

那么以 r o o t root root为根去遍历整棵树,枚举每对点对 ( u , v ) (u,v) (u,v)计算贡献

u u u v v v的儿子且满足 v > u v>u v>u,答案直接加一(只有祖先节点被标记自己才会被标记)

v v v u u u的儿子且满足 u > v u>v u>v,答案直接加一

否则求一个 u , v u,v u,v l c a lca lca x x x,那么考虑 x x x被染色,接下来想染色 u , v u,v u,v需要走不同的分支

x x x u u u需要 i i i步, x x x v v v需要 j j j

u > v u>v u>v时答案加上 f [ i ] [ j ] f[i][j] f[i][j]

v > u v>u v>u时答案加上 f [ j ] [ i ] f[j][i] f[j][i]

定义 f [ i ] [ j ] f[i][j] f[i][j]表示一条链长 i i i,一条链长 j j j,链 i i i先到的概率

初始化 f [ 0 ] [ j ] = 1 f[0][j]=1 f[0][j]=1

然后 f [ i ] [ j ] = f [ i − 1 ] [ j ] ∗ 1 2 + f [ i ] [ j − 1 ] ∗ 1 2 f[i][j]=f[i-1][j]*\frac{1}{2}+f[i][j-1]*\frac{1}{2} f[i][j]=f[i1][j]21+f[i][j1]21

意思是若第一步选的是长为 i i i的链,那么问题转化为长 i − 1 i-1 i1和长 j j j链的概率问题

第一步选的是 j j j就转化为长 i i i和长 j − 1 j-1 j1链的概率问题

这部分代码是

void init()
{
	int pro = quick(2,mod-2);
	for(int i=1;i<=n;i++)	f[0][i] = 1;
	for(int i=1;i<=n;i++)
	for(int j=1;j+i<=n;j++)
		f[i][j] = ( 1ll*f[i-1][j]+f[i][j-1] )*pro%mod;
}

时间复杂度为 O ( n 3 l o g 2 ( n ) ) O(n^3log_2(n)) O(n3log2(n))

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
const int mod = 1e9+7;
const int maxn = 209;
int n,pro;
vector<int>vec[maxn];
int quick(int x,int n)
{
	int ans = 1;
	for( ; n ; n>>=1,x=1ll*x*x%mod )
		if( n&1 )	ans = 1ll*ans*x%mod;
	return ans;
}
int f[209][209],sf[209][209];
void init()
{
	int pro = quick(2,mod-2);
	for(int i=1;i<=n;i++)	f[0][i] = 1;
	for(int i=1;i<=n;i++)
	for(int j=1;j+i<=n;j++)
		f[i][j] = ( 1ll*f[i-1][j]+f[i][j-1] )*pro%mod;
}
int fa[maxn][22],deep[maxn];
int lca(int x,int y)
{
	if( deep[x]<deep[y] )	swap(x,y);
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )	x = fa[x][i];
	if( x==y )	return x;
	for(int i=20;i>=0;i--)
		if( fa[x][i]!=fa[y][i])		x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
int get_dis(int x,int y)
{
	int u=lca(x,y);
	return deep[x]+deep[y]-2*deep[u];
}
void predfs(int u,int father)
{
	fa[u][0] = father; deep[u] = deep[father]+1;
	for(int i=1;i<=20;i++)
		fa[u][i] = fa[fa[u][i-1]][i-1];
	for( auto v:vec[u] )
		if( v!=father )	predfs(v,u);
}
int solve()
{
	int ans = 0;
	for(int u=1;u<=n;u++)
	for(int i=u+1;i<=n;i++)
	{
		int x = lca(u,i), q = get_dis(i,x), w = get_dis(u,x);
		if( x==u )	ans = ( ans+(u>i) )%mod;
		else if( x==i )	ans = ( ans+(i>u) )%mod;
		else
		{
			if( i>u )	ans = ( ans+f[q][w] )%mod;
			else	ans = ( ans+f[w][q] )%mod;
		}
	}
	return ans;
}
signed main()
{
	cin >> n;
	init();
	for(int i=1;i<n;i++)
	{
		int l,r; cin >> l >> r;
		vec[l].push_back( r );
		vec[r].push_back( l );
	}
	int res = 0;
	for(int i=1;i<=n;i++)
	{
		predfs(i,0);
		res = ( 1ll*res+1ll*solve()*quick(n,mod-2)%mod )%mod;
	}
	cout << ( res%mod+mod )%mod;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值