题解——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 ;
}

如有不足,请大佬指出

原文地址:https://www.cnblogs.com/ssw02/p/11426124.html