USACO superbull(MST)

题意:n支队伍,每支队伍权值a[i],队伍i和队伍j打比赛会产生a[i] xor a[j]这么多精彩度,但每举行一场比赛你必须淘汰其中一支队伍,淘汰的队伍不能再参赛,请你安排一系列比赛使得最后能够产生冠军,并且使得精彩度最大。n<=2000。

容易发现最多打(n-1)场比赛。我考试时乱做的,就把所有边排序选前n-1条。

其实n-1条边连接了n个点,很明显是一棵树,所以跑一下最大生成树就行了。由于是完全图,所以产生的边会很对,用prim比kruskal更优。


然后实现的时候我用的自创的用来优化dijkstra的数据结构,可以把空间复杂度严格限制在O(n),在完全图中效果特别明显,用堆的都是几十MB,我1MB,并且快很多。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
const int MAXN = 2005;
int a[MAXN], N;

int dis[MAXN], maxp[MAXN*2];
bool used[MAXN];
inline void pushup(int&p) {
	int &r = maxp[p];
	r = p * !used[p];
	if (dis[maxp[p<<1]] > dis[r]) r = maxp[p<<1];
	if (dis[maxp[p<<1|1]] > dis[r]) r = maxp[p<<1|1];
}
void relax(int p, int d) {
	if (d < dis[p]) return;
	dis[p] = d;
	for (int i = p; i; i>>=1) pushup(i);
}
void finish(int p) {
	used[p] = 1;
	for (int i = p; i; i>>=1) pushup(i);
}

LL Prim() {
	relax(1, 0);
	LL res = 0;
	for (int i = 1; i<=N; ++i)
	{
		int u = maxp[1];
		res += dis[u];
		finish(u);
		for (int j = 1; j<=N; ++j)
			if (j != u && !used[j]) relax(j, a[j]^a[u]);
	}
	return res;
}

int main()
{
	cin >> N;
	for (int i = 1; i<=N; ++i) cin >> a[i];
	cout << Prim() << '\n';
	return 0;
}


你可能感兴趣的:(USACO superbull(MST))