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