AUOJ_1776尘星的标坐叉交题解(复杂条件的简化和归类)

传送门

题意有些复杂,这里不再阐述。

题解

​ 考虑题目给出的式子,因为这个式子有三项,有些麻烦,考虑化简。

​ 如果我们把深度重定义, \(\sum_{i\in A}{d_i}\) 也就可以转化成 \(\sum_{i\in A}{\sum_{j\in A}{[j是i的祖先]}}\)

​ 那么此时我们再考虑任意一个A中的点对 \((i,j)\)

  • \((i,j)\) 没有祖先关系,产生1的贡献。
  • \((i,j)\)​ 有祖先关系,根据点权产生 \(1\)\(2\)​ 的贡献。

​ 也就是说,A中任意一个点对都会产生至少 \(1\) 的贡献,把含点权的那一部分专门提取出来考虑。

​ 考虑树上遍历时用树状数组维护,得到每个点的 \(up\) 数组(自己至根路径上有多少比自己点权大的点)和 \(dn\) 数组(自己子树内有多少比自己点权大的点)。

​ 因为刚开始所有的点都在 \(A\) 中,所以所有的点的点权部分贡献都是 \(up\),因为要让不满序列字典序最小,我们把每个点的 \(dn-up\) 排序,之后再逐个访问输出就行了。

​ 为什么这样可以呢,会不会出现点对两点分属不同集合造成影响的情况?不会。因为我们可以发现,在选择点的过程中,如果一个点 \(u\) 被选取,它的子树中比它大的一定已经被都被选取了。这是因为我们始终要保证字典序最小,先选了比它大的,可以把贡献延后添加。

​ 这题就没了。

代码

#include <bits/stdc++.h>
using namespace std ;
#define ll long long
#define rep(i,l,r) for(ll i=(l);i<=(r);++i)
#define per(i,r,l) for(ll i=(r);i>=(l);--i)
#define wif while
const ll inf = INT_MAX , df = 1e6 + 7 , N = 5e5 + 7 ;
struct edge	{	ll to , nxt ;	} e[ df << 1 ] ;
ll i,j,k,l,m,n,o=1,p,q,r,s,t,u,v,w,x,y,z,head[df],val[df],up[df],dn[df],ans,a[df];
ll ma[df][2] ;
inline void add( ll u , ll v )	{
	return e[ ++ o ] = ( edge ) { v , head[u] } , head[u] = o , void() ;	}
inline ll read()	{
	ll x = 0 , y = 1 ;	char ch = getchar() ;
	wif( ch > '9' || ch < '0' )		y = ( ch == '-' ) ? - 1 : 1 , ch = getchar() ;
	wif( ch >= '0' && ch <= '9' )	x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar() ;
	return x * y ;	}
ll qry( ll x , ll y )	{	ll ret = 0 ;
	for( ll i = x ; i <= N ; i += ( i & ( - i ) ) )	ret += ma[i][y] ;
	return ret ;	}
void upd( ll x , ll y , ll z )	{
	for( ll i = x ; i ; i -= ( i & ( - i ) ) )	ma[i][z] += y ;	}
void dfs( ll u , ll lst )	{
	up[u] = qry( val[u] + 2 , 0 ) , dn[u] = - qry( val[u] + 2 , 1 ) ;
	upd( val[u] + 1 , 1 , 0 ) , upd( val[u] + 1 , 1 , 1 ) ;
	for( ll i = head[u] , v ; i ; i = e[i].nxt )	{
		v = e[i].to ;	if( v == lst )	continue ;
		dfs( v , u ) ;	}
	dn[u] += qry( val[u] + 2 , 1 ) ;	upd( val[u] + 1 , - 1 , 0 ) ;
	return ;	}
int main()	{
	n = read() , o = 1 ;
	rep(i,1,n)	val[i] = read() ;
	rep(i,2,n)	{
		ll u = read() , v = read() ;
		add( u , v ) , add( v , u ) ;
	}
	dfs( 1 , 0 ) ;
	rep(i,1,n)	a[i] = dn[i] - up[i] , ans += up[i] ;
	sort( a + 1 , a + n + 1 ) ;
	printf( "%lld " , ans + n * ( n - 1 ) / 2 ) ;
	rep(i,1,n)	{
		ans += a[i] ;
		printf("%lld ",ans+(n-i)*(n-i-1)/2);
	}
	puts("");
	return 0 ;	}
posted @ 2021-08-03 10:37  red_giant_bear  阅读(73)  评论(0)    收藏  举报