说在前面
第二次写线段树合并=w=
但是对复杂度还不是很明白。看到zyf2000的blog里有提到:深度是
log(N)
的
N
条链,时间复杂度上限是
写的时候状态不怎么好,把Insert函数里一个小于等于写成了大于等于,用printf查了好久才发现…
题目
BZOJ3702传送门
BZOJ2212传送门
(没错,这是一道双倍经验题)
题面
现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。
要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少。
输入输出格式
输入格式:
第一行一个整数N,表示叶子节点个数。
下面每行包含一个整数x
如果x为0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息。
如果x不为0,表示这个节点是叶子节点,且该节点权值为x。
输出格式:
输出交换后最少的逆序对个数
解法
算是一道比较基础的线段树合并题
注意到题目说是可以随意交换子树的。那么对于一个非叶子节点u来说(假设两个儿子是x和y,并且x和y均已处理完毕),x和y要么保持原样要么交换,把这两种方式的代价取min累加到答案里就可以了,而不需要真正的去交换子树,因为子树内的顺序对子树之间的贡献是没有影响的(me略傻,这里想了很久才想明白=A=)。
那么计算代价的话用值域线段树
如果把x放在y的左边,那么对答案有贡献的部分就是x->Rsize
∗
y->Lsize
如果把y放在x的左边,那么对答案有贡献的部分就是y->Rsize
在合并线段树递归的时候顺便计算
下面是自带大常数的代码
调试信息懒得删除了…
需要看代码的话,还是复制到本地的编译器上看吧=w=
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N ;
struct Tree{
int val ;
Tree *ls , *rs ;
}tc[400005] , *ttc = tc , *treeRoot;
struct Node{
long long siz ;
Node *ls , *rs ;
void update(){
siz = 0 ;
if( ls ) siz += ls->siz ;
if( rs ) siz += rs->siz ;
}
}*root[400005] , w[400005*20] , *tw = w ;
void readTree( Tree *&nd ){
if( !nd ) nd = ++ttc ;
scanf( "%d" , &nd->val ) ;
if( nd->val ) return ;
readTree( nd->ls ) ;
readTree( nd->rs ) ;
}
void Insert( Node *&nd , int lf , int rg , int val ){
if( !nd ) nd = ++tw ;
if( lf == rg ){
nd->siz = 1 ;
return ;
}
int mid = ( lf + rg ) >> 1 ;
if( val <= mid ) Insert( nd->ls , lf , mid , val ) ;
else Insert( nd->rs , mid+1, rg , val ) ;
nd->update() ;
}
long long Lcost , Rcost , ans ;
/*void print( Node *nd ){
printf( "(%lld %lld)\n", nd->ls?nd->ls->siz:0 , nd->rs?nd->rs->siz:0 ) ;
}*/
void Merge( Node *&nd , Node *x , Node *y , int lf , int rg ){
// printf( "%d %d %d [%d %d]\n" , nd , x , y , lf , rg ) ;
if( !x ){
nd = y ;
// printf( "[%d %d] No x at %lld nd(%d)\n" , lf , rg , y?y->siz:0 , nd ) ;
return ;
}
if( !y ){
nd = x ;
// printf( "[%d %d] No y at %lld nd(%d)\n" , lf , rg , x->siz , nd ) ;
return ;
}
if( !nd ) nd = ++tw ;
int mid = ( lf + rg ) >> 1 ;
if( x->rs && y->ls ) Lcost += 1LL * x->rs->siz * y->ls->siz ;
if( y->rs && x->ls ) Rcost += 1LL * y->rs->siz * x->ls->siz ;
// print( nd ) ;
Merge( nd->ls , x->ls , y->ls , lf , mid ) ;
// print( nd ) ;
// printf( "pot (%d %d)\n" , nd->ls , nd->rs ) ;
Merge( nd->rs , x->rs , y->rs , mid+1 , rg ) ;
// print( nd ) ;
// printf( "pot (%d %d)\n" , nd->ls , nd->rs ) ;
nd->update() ;
}
/*void print( Node *nd , int lf , int rg ){
int mid = ( lf + rg ) >> 1 ;
if( nd->ls ) print( nd->ls , lf , mid ) ;
printf( "%d [%d %d] siz(%lld)\n" , nd , lf , rg , nd->siz ) ;
if( nd->rs ) print( nd->rs , mid+1, rg ) ;
}*/
void dfs( Tree *nd ){
if( !nd || nd->val ) return ;
dfs( nd->ls ) ; dfs( nd->rs ) ;
Lcost = Rcost = 0 ;
// printf( "Mer : %d %d %d\n" , nd-tc , nd->ls-tc , nd->rs-tc ) ;
/* if( nd->ls-tc == 2 ){
print( root[ nd->ls-tc ] , 1 , N ) ;
print( root[ nd->rs-tc ] , 1 , N ) ;
}
*/
Merge( root[nd-tc] , root[ nd->ls-tc ] , root[ nd->rs-tc ] , 1 , N ) ;
// printf( "Mer ended , siz = %lld\n" , root[nd-tc]->siz ) ;
ans += min( Lcost , Rcost ) ;
}
void solve(){
for( Tree *tmp = tc + 1 ; tmp <= ttc ; tmp ++ )
if( tmp->val ) Insert( root[tmp-tc] , 1 , N , tmp->val ) ;
dfs( treeRoot ) ;
printf( "%lld" , ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
readTree( treeRoot ) ;
solve() ;
}