题目
点这里看题目。
分析
感觉自己好蠢
假如我们有两棵树(T_1,T_2),我们应该怎么计算出它们合并之后的最优解呢?
设最优情况下,(T_1)的所有内存段的集合为(M_1),(T_2)的集合为(M_2)。我们可以知道,(M_1,M_2)中所有的元素都是不能再合并的(废话)。
考虑有(m_1,m_2in M_1,m_1>m_2),有(m_3,m_4in M_2, m_3>m_4),我们应该如何分配?我们只能:
1.最大值和次大值组合,得到的内存为(max{m_1,m_4}+max{m_2,m_4});
2.最大值和最大值组合,次大值和次大值组合,得到的内存为(max{m_1,m_3}+max{m_2,m_4})。
很显然,方案 2 更优,可以通过枚举所有情况证明。
于是我们就可以想到,用堆维护(M_1)和(M_2)。每次取出两个集合中的最大值,合并起来,放到合并结果的堆里面。最后有剩余的,直接塞进新的堆里面。
很不幸,这样做是(O(sum_u d_u imes log_2n))的,我觉得会 TLE ,其中(d_u)为(u)的深度。
考虑优化。我们每次其实只需要合并,得到合并过后的堆就好。因此,我们可以直接将较小的堆合并到较大的里面,然后就得到了合并结果。这就是启发式合并。
这样做的时间复杂度(O(nlog_2n))?我怎么觉得挺像(O(nlog_2^2n))的?
代码
#include <queue>
#include <cstdio>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 5;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
template<typename _T>
void swapp( _T &x, _T &y )
{
_T t = x; x = y, y = t;
}
template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
}
struct edge
{
int to, nxt;
}Graph[MAXN << 1];
priority_queue<int> q[MAXN];
int stk[MAXN], top;
int head[MAXN], M[MAXN], id[MAXN];
int N, cnt;
void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
int merg( int x, int y )
{
if( q[x].size() < q[y].size() ) swapp( x, y );
while( ! q[y].empty() )
stk[++ top] = MAX( q[x].top(), q[y].top() ),
q[x].pop(), q[y].pop();
while( top ) q[x].push( stk[top --] );
return x;
}
void DFS( const int u )
{
id[u] = u;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
{
DFS( v = Graph[i].to );
id[u] = merg( id[u], id[v] );
}
q[id[u]].push( M[u] );
}
int main()
{
read( N );
for( int i = 1 ; i <= N ; i ++ ) read( M[i] );
for( int i = 2, f ; i <= N ; i ++ ) read( f ), addEdge( f, i );
DFS( 1 );
LL ans = 0;
while( ! q[id[1]].empty() ) ans += q[id[1]].top(), q[id[1]].pop();
write( ans ), putchar( '
' );
return 0;
}