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 ; }

浙公网安备 33010602011771号