[BZOJ3451]-[Tyvj1953]Normal-点分+FFT辅助统计

说在前面

并没有什么想说的,但是要保持格式=w=


题目

BZOJ3451传送门

题目大意

我们定义这样一种点分治的写法:

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。在点分树里面看的话,每个点的贡献就是它的深度
再放大一点,对于任意一对点 u , v ,如果 u v 的路径上, u 是第一个被选择的点,那么就会获得1的贡献。这样这就变成了一个树链统计问题

现在来考虑期望
期望的话呢,也是一样的。首先每个点都肯定会把自己统计进去,这就一定会有N的收益
然后考虑两个点 u , v     ( u ! = v ) 的情况,显然它对期望的贡献是 2 d i s t ( u , v ) ,因为这条链上,只要 u 或者 v 被先选,那么就会贡献 1

然后点分的时候,统计一下从当前分治中心往下走的 各个长度 有多少条链,把这个数组卷一卷就是路径的条数了
很简单的,就不扯那么多了……


下面是自带大常数的代码

#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() ;
}

猜你喜欢

转载自blog.csdn.net/Izumi_Hanako/article/details/80003063