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;
    }
    

     

  • 相关阅读:
    MDX Step by Step 读书笔记(六) Building Complex Sets (复杂集合的处理) Filtering Sets
    在 Visual Studio 2012 开发 SSIS,SSAS,SSRS BI 项目
    微软BI 之SSIS 系列 在 SSIS 中读取 SharePoint List
    MDX Step by Step 读书笔记(五) Working with Expressions (MDX 表达式) Infinite Recursion 和 SOLVE_ORDER 原理解析
    MDX Step by Step 读书笔记(五) Working with Expressions (MDX 表达式)
    使用 SQL Server 2012 Analysis Services Tabular Mode 表格建模 图文教程
    MDX Step by Step 读书笔记(四) Working with Sets (使用集合) Limiting Set and AutoExists
    SQL Server 2012 Analysis Services Tabular Model 读书笔记
    Microsoft SQL Server 2008 MDX Step by Step 学习笔记连载目录
    2011新的开始,介绍一下AgileEAS.NET平台在新的一年中的发展方向
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8611948.html
Copyright © 2011-2022 走看看