zoukankan      html  css  js  c++  java
  • 【bzoj3451】Tyvj1953 Normal 期望+树的点分治+FFT

    题目描述

    给你一棵 $n$ 个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心。定义消耗时间为每层分治的子树大小之和,求消耗时间的期望。

    输入

    第一行一个整数n,表示树的大小
    接下来n-1行每行两个数a,b,表示a和b之间有一条边
    注意点是从0开始标号的

    输出

    一行一个浮点数表示答案
    四舍五入到小数点后4位
    如果害怕精度跪建议用long double或者extended

    样例输入

    3
    0 1
    1 2

    样例输出

    5.6667


    题解

    期望+树的点分治+FFT

    由于期望可加,因此所求等于 $sumlimits_{i=1}^nsumlimits_{j=1}^nP(j在i的点分树子树内)$ 。

    而 $j$ 在 $i$ 的点分树子树内,又相当于:$i$ 到 $j$ 的路径上的所有点中(包括 $i$ 和 $j$),$i$ 是第一个选择的。因为如果其它点先被选择则会将 $i$ 与 $j$ 分开,使得 $j$ 不在 $i$ 的点分树内。

    这些点中,显然每个点作为第一个选择的点的概率都是相等的,因此概率为 $frac 1{dis(i,j)}$ ( $dis(i,i)=1$ )。

    所求转化为求 $sumlimits_{i=1}^nsumlimits_{j=1}^ndis(i,j)$ ,即求距离等于每个值的点对数目。

    考虑点分治,每次统计经过根节点路径的答案。dfs一遍子树得出距离等于每个值的点的个数,用容斥(任选两个 - 在同一棵子树内选两个)的方法得出答案。

    容易发现求答案的过程实际上就是自身与自身求卷积,因此使用FFT快速求解。

    由于距离范围(多项式次数)不会超过子树大小,因此时间复杂度为 $T(n)=O(nlog n)+2(T(frac n2)+O(frac n2log n))=O(nlog^2n)$ 

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 30010
    using namespace std;
    const double pi = acos(-1);
    struct data
    {
    	double x , y;
    	data() {}
    	data(double a , double b) {x = a , y = b;}
    	data operator+(const data &a)const {return data(x + a.x , y + a.y);}
    	data operator-(const data &a)const {return data(x - a.x , y - a.y);}
    	data operator*(const data &a)const {return data(x * a.x - y * a.y , x * a.y + y * a.x);}
    }A[65550];
    int head[N] , to[N << 1] , next[N << 1] , cnt , vis[N] , si[N] , ms[N] , sum , root , deep[N] , val[N] , tot , num[N];
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void getroot(int x , int fa)
    {
    	int i;
    	si[x] = 1 , ms[x] = 0;
    	for(i = head[x] ; i ; i = next[i])
    		if(!vis[to[i]] && to[i] != fa)
    			getroot(to[i] , x) , si[x] += si[to[i]] , ms[x] = max(ms[x] , si[to[i]]);
    	ms[x] = max(ms[x] , sum - si[x]);
    	if(ms[x] < ms[root]) root = x;
    }
    void getdeep(int x , int fa)
    {
    	int i;
    	val[++tot] = deep[x];
    	for(i = head[x] ; i ; i = next[i])
    		if(!vis[to[i]] && to[i] != fa)
    			deep[to[i]] = deep[x] + 1 , getdeep(to[i] , x);
    }
    void fft(data *a , int n , int flag)
    {
    	int i , j , k;
    	for(i = k = 0 ; i < n ; i ++ )
    	{
    		if(i > k) swap(a[i] , a[k]);
    		for(j = n >> 1 ; (k ^= j) < j ; j >>= 1);
    	}
    	for(k = 2 ; k <= n ; k <<= 1)
    	{
    		data wn(cos(2 * pi * flag / k) , sin(2 * pi * flag / k));
    		for(i = 0 ; i < n ; i += k)
    		{
    			data w(1 , 0) , t;
    			for(j = i ; j < i + (k >> 1) ; j ++ , w = w * wn)
    				t = w * a[j + (k >> 1)] , a[j + (k >> 1)] = a[j] - t , a[j] = a[j] + t;
    		}
    	}
    	if(flag == -1)
    		for(i = 0 ; i < n ; i ++ )
    			a[i].x /= n;
    }
    void calc(int flag)
    {
    	int i , mx = 0 , n = 1;
    	for(i = 1 ; i <= tot ; i ++ ) mx = max(mx , val[i]);
    	while(n <= 2 * mx) n <<= 1;
    	for(i = 0 ; i < n ; i ++ ) A[i].x = A[i].y = 0;
    	for(i = 1 ; i <= tot ; i ++ ) A[val[i]].x ++ ;
    	fft(A , n , 1);
    	for(i = 0 ; i < n ; i ++ ) A[i] = A[i] * A[i];
    	fft(A , n , -1);
    	for(i = 0 ; i <= 2 * mx ; i ++ ) num[i] += flag * (int)(A[i].x + 0.1);
    }
    void dfs(int x)
    {
    	int i;
    	vis[x] = 1 , deep[x] = tot = 0 , getdeep(x , 0) , calc(1);
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(!vis[to[i]])
    		{
    			deep[to[i]] = 1 , tot = 0 , getdeep(to[i] , 0) , calc(-1);
    			sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , dfs(root);
    		}
    	}
    }
    int main()
    {
    	int n , i , x , y;
    	long double ans = 0;
    	scanf("%d" , &n);
    	for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x + 1 , y + 1) , add(y + 1 , x + 1);
    	sum = ms[0] = n , root = 0 , getroot(1 , 0) , dfs(root);
    	for(i = 0 ; i < n ; i ++ ) ans += (long double)num[i] / (i + 1);
    	printf("%.4Lf
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    Xcode 统计代码行数
    AWS 根用户MFA丢失后如何处理
    istio 基础入门
    AWS 如何挑选合适EC2实例类型
    word去除页眉首页横线
    word 题注 图注 表注 交叉引用 自动编号
    (转)Python基础热图-参数超级详解
    VScode 运行代码显示:Code is already running!
    pyside2安装避坑
    vscode import numpy error:DLL load failed: The specific module could not be found
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8611948.html
Copyright © 2011-2022 走看看