[十二省联考2019]春节十二响

题目

点这里看题目。

分析

感觉自己好蠢

假如我们有两棵树(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;
}
原文地址:https://www.cnblogs.com/crashed/p/13131219.html