说在前面
并没有什么想说的,但是要保持格式=w=
题目
题目大意
我们定义这样一种点分治的写法:
long long tot_siz = 0 ;
void div_and_conquer( int u ){
dfs_get_siz( u ) ;//求出子树大小
tot_siz += siz[u] ;
if( siz[u] == 1 ) return ;
for( each son v )
div_and_conquer( x ) //x是v子树里一个随机的点
}
称之为:随机点分治
现在给出树的形态,请求出tot_siz
的期望。保证树的节点数不超过30000
输入输出格式
输入格式:
第一行一个整数N,表示节点数
接下来N-1行,每行两个数u , v,描述一条树边
输出格式:
输出答案,四舍五入保留4位小数
解法
其实这个题,只要能把贡献转化一下,然后就是个撒币题了
先考虑把题面上的tot_siz
转化一下
按照题目的意思,每到一个子树,就要累计上这个子树的size。在点分树里面看的话,每个点的贡献就是它的深度
再放大一点,对于任意一对点
,如果
到
的路径上,
是第一个被选择的点,那么就会获得1的贡献。这样这就变成了一个树链统计问题
现在来考虑期望
期望的话呢,也是一样的。首先每个点都肯定会把自己统计进去,这就一定会有N的收益
然后考虑两个点
的情况,显然它对期望的贡献是
,因为这条链上,只要
或者
被先选,那么就会贡献
然后点分的时候,统计一下从当前分治中心往下走的 各个长度 有多少条链,把这个数组卷一卷就是路径的条数了
很简单的,就不扯那么多了……
下面是自带大常数的代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const double PI2 = 3.1415926535897932384626 * 2 ;
double ans ;
int N , tp , head[30005] ;
struct Path{
int pre , to ;
}p[60005] ;
typedef struct Complex cpx ;
struct Complex {
double r , i ;
Complex( const double r_ = 0 , const double i_ = 0 ):r(r_),i(i_){} ;
cpx operator + ( const cpx &A ) const { return cpx( r + A.r , i + A.i ) ; }
cpx operator - ( const cpx &A ) const { return cpx( r - A.r , i - A.i ) ; }
cpx operator * ( const cpx &A ) const { return cpx( r * A.r - i * A.i , r * A.i + i * A.r ) ; }
cpx operator * ( double p ) const { return cpx( r * p , i * p ) ; }
cpx operator / ( double p ) const { return cpx( r / p , i / p ) ; }
} a[70005] , b[70005] , nil ;
void In( int t1 , int t2 ){
p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}
bool vis[30005] ;
int Gr , SIZ , siz[30005] , maxs[30005] ;
void dfsGr( int u , int f ){
maxs[u] = 0 , siz[u] = 1 ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( vis[v] || v == f ) continue ;
dfsGr( v , u ) , siz[u] += siz[v] ;
if( maxs[u] < siz[v] ) maxs[u] = siz[v] ;
} maxs[u] = max( maxs[u] , SIZ - siz[u] ) ;
if( maxs[Gr] > maxs[u] ) Gr = u ;
}
int maxd , dep[30005] ;
int sta[30005] , topp , in[30005] , out[30005] ;
void dfs( int u , int f ){
sta[++topp] = u ;
in[u] = topp ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( vis[v] || v == f ) continue ;
dep[v] = dep[u] + 1 , dfs( v , u ) ;
} if( maxd < dep[u] ) maxd = dep[u] ;
out[u] = topp ;
}
void FFT( cpx *a , int len , int rev ){
for( int i = 1 , now = 0 , t ; i < len ; i ++ ){
t = ( len >> 1 ) ;
while( now&t ) now ^= t , t >>= 1 ; now ^= t ;
if( now > i ) swap( a[now] , a[i] ) ;
}
for( int i = 2 ; i <= len ; i <<= 1 ){
cpx wid = cpx( cos( PI2 / i ) , sin( PI2 * rev / i ) ) , w ;
for( int j = 0 ; j < len ; j += i ){
w = cpx( 1 , 0 ) ;
for( int k = j ; k < j + ( i >> 1 ) ; k ++ ){
cpx t0 = a[k] , t1 = a[k+(i>>1)] * w ;
a[k] = t0 + t1 ;
a[k+(i>>1)] = t0 - t1 ;
w = w * wid ;
}
}
} if( rev == -1 ) for( int i = 0 ; i < len ; i ++ )
a[i] = a[i] / len ;
}
double calc( int v ){
double rt = 0 ;
int len , pas = in[v] , ed = out[v] , md1 = 0 , md2 = 0 ;
for( int i = 1 ; i < pas ; i ++ )
a[ dep[sta[i]] ].r ++ , md1 = max( md1 , dep[sta[i]] ) ;
for( int i = pas ; i <= ed ; i ++ )
b[ dep[sta[i]] ].r ++ , md2 = max( md2 , dep[sta[i]] ) ;
for( len = 1 ; len <= ( md1 + md2 ) ; len <<= 1 ) ;
FFT( a , len , 1 ) ; FFT( b , len , 1 ) ;
for( int i = 0 ; i < len ; i ++ ) a[i] = a[i] * b[i] ;
FFT( a , len ,-1 ) ;
// printf( "%.2f %.2f %.2f\n" , a[0].r , a[1].r , a[2].r ) ;
for( int i = 0 ; i < len ; i ++ )
rt += 2 * a[i].r / ( i + 1 ) , a[i] = b[i] = nil ;
return rt ;
}
void div_and_conquer( int u ){
vis[u] = true ;
topp = dep[u] = maxd = 0 , dfs( u , u ) ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( !vis[v] ) ans += calc( v ) ;
}
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( vis[v] ) continue ;
SIZ = siz[v] , Gr = 0 , dfsGr( v , v ) ;
div_and_conquer( Gr ) ;
}
}
void solve(){
maxs[ Gr = 0 ] = 0x3f3f3f3f ;
SIZ = N , dfsGr( 1 , 1 ) ;
div_and_conquer( Gr ) ;
printf( "%.4f" , ans + N ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 , a , b ; i < N ; i ++ ){
scanf( "%d%d" , &a , &b ) ;
In( a + 1 , b + 1 ) ;
} solve() ;
}