• 题解——single(二次扫描+数学)


    题解——single(二次扫描+数学)

    *数学题,然后用二次扫描优化即可 *


    题面

    Description
    题面已隐藏

    Input
    一棵树+一堆杂七腊八的东西

    Output
    一个整数表示最低花费

    in.1
    2
    1 2
    17 31
    2
    1 2
    0
    31 17

    out.1
    31 17
    17 31

    数据范围与约定

    对于100%的数据,T=5, 2<=n<=100000,1<=u,v<=n,保证给出的n-1条边形成一棵树
    对于100%的数据,t=0或t=1,1<=a[i]<=100,1<=b[i]<=10^9,t=1时保证给出的b数组对应唯一的一个a数组。
    对于100%的数据,单个输入文件不会包含超过2000000个整数,这段话可以理解为,你不必考虑输入输出对程序运行时间的影响。
    对于100%的数据,保证答案不会超过int能表示的范围

    思路

    主要思路

    10分做法
    对于每一点进行一次Dfs,处理出深度dep[]数组。然后对每个点暴力统计贡献即可, N^2复杂度

    30分做法
    推导:我们明显发现在不断进行dfs的时候,并没有必要每个点都这样做,二次扫描可以很好的在 O(n) 的复杂度处理完 。 具体就是在 fa 向 v 转移的时候,实际上 fa 所对应的的子树部分(除去v的部分)贡献都增加 ∑ , 而 v 的子树部分贡献都减少 ∑ 。
    b[ v ] = b[ fa ] + tot - sum[ v ]*2 ( sum[]表示子树 ∑ a[ i ] )

    60分做法
    又上推导可知,在知道 b 数组的情况下,只要列出 N-1 个类方程 , 然后用 N^3 的高斯消元来做。

    100分做法
    注意到边长均为 1 , b[ root ] = ∑ sum[ i ] , 这样每个点的贡献刚好是都计算一遍 。 然后一个类似差分的思想。
    ∑( b[ u ] - b[ fa ] )+ 2*b[ root ] = tot*( n-1 )

    最后处理一下 a[ 1 ] , 用之前解出来的减下就好 。

    细节

    作为一个OIer,要时刻保持对出题人的尊敬 , 尤其是 Liu_runda

    每个 b[ i ]作为 1 ~ 1e9 的 int , 多来几个就炸 int 了呀. (liu_runda的惯用的题目卡分方法,屡试不爽 )

    #include<bits/stdc++.h>
    using namespace std; 
    const int MAXN = 100005 ; 
    inline int read(){
    	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ;return s ; 
    }
    int head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] , dep[ MAXN ] , tot = 1 , fa[ MAXN ] , sum[ MAXN ] , du[ MAXN ];
    int N , T  , a[ MAXN ] , b[ MAXN ] , root , size[ MAXN ] ;
    long long totnum = 0 ;
    void  add( int x , int y ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
    } 
    // part a
    void  dfs( int u , int  father ){
    	fa[ u ] = father ;
    	sum[ u ] = a[ u ] ;
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( to[ i ] == father )continue ;
    		dep[ to[i] ] = dep[ u ] + 1 ; 
    		dfs( to[ i ] , u ) ;
    		sum[ u ] += sum[ to[i] ] ;
    	}
    }
    void  dfs2( int  u , int  father ){
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( to[ i ] == father )continue ;
    		b[ to[ i ] ] = b[ u ] + sum[ 1 ] - 2*sum[ to[i] ] ;
    		dfs2( to[ i ] , u ) ;
    	}
    }
    // part b 
    void  dfs3( int u , int father ){
    	for( int i = head[ u ] ; i ; i= nex[ i ] ){
    		if( to[ i ] == father )continue ; 
    		dfs3( to[ i ] , u ) ;
    		totnum += ( b[ u ] - b[ to[i] ] ) ;//累计差分
    	}
    }
    
    void  dfs4( int u , int father ){
    	long long tot = 0;
    	for(int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( father == to[ i ] ) continue;
    		size[ to[i] ] = ( b[ u ] - b[ to[ i ] ] + totnum ) / 2 ; 
    		tot += size[ to[i] ] ;
    		dfs4( to[ i ] , u ) ;
    	}
    	a[ u ] = size[ u ] - tot ;
    } 
    void  get_ab(){
    	for( int i = 1 ; i <= N ; ++i )a[ i ] = read() ; 
    	dfs( 1 , 1 ) ;
    	for( int i = 1 ; i <= N ; ++i )b[ 1 ] += dep[ i ]*a[ i ] ;
    	dfs2( 1 , 1 ) ;
    	for( int i = 1 ; i <= N ; ++i )printf("%d ",b[ i ] );printf("
    ") ;
    }
    void  get_ba(){
    	for( int i = 1 ; i <= N ; ++i )b[ i ] = read() ;
    	dfs3( 1 , 1 ) ;
    	totnum =( b[ 1 ]*2 - totnum ) / ( N-1 ) ;
    	dfs4( 1 , 1 ) ;
    	for( int i = 2 ; i <= N ; ++i )totnum -= a[ i ] ; printf("%d ",totnum ) ;
    	for( int i = 2 ; i <= N ; ++i )printf("%d ", a[ i ] ) ;
    	printf("
    ");
    }
    void  clear(){//实际上没必要清那么多
    	for( int i = 1 ; i <= N ; ++i )size[ i ] = du[ i ] = sum[ i ] = dep[ i ] = head[ i ] = 0 ;
    	tot = 1 , b[ 1 ] = 0 , totnum = 0 ;
    }
    int main(){
    	freopen("single.in","r",stdin);
    	freopen("single.out","w",stdout);
    	T = read() ; 
    	while( T-- ){
    		N = read() ; int m1 , m2 ; 
    	    for( int i = 1 ; i < N ; ++i ){
    		    m1 = read() , m2 = read() ; add( m1 , m2 ) , add( m2 , m1 ) ;
    		    du[ m1 ]++ , du[ m2 ]++ ;
    	    }
    	    m1 = read() ; 
    	    if( !m1 )get_ab();
    	    else get_ba();
    	    clear() ;
    	}
    	return 0 ;
    }
    

    如有不足,请大佬指出

  • 相关阅读:
    ubuntu ping响应慢的解决方法
    Linux串口中的超时设置
    GSM07.10协议中串口复用使用的校验算法
    交叉编译中的build、host和target
    mount img
    修改mysql默认字符编码出现的Job failed to start解决方法
    ubuntu下建立NFS共享,并用开发板挂载
    Linux上进行单片机开发
    LwIP移植和使用
    [buuctf] pwnrip
  • 原文地址:https://www.cnblogs.com/ssw02/p/11426124.html
Copyright © 2020-2023  润新知