6023. 【GDOI2019模拟2019.2.16】烤仓鼠(Trie + 贪心 + set)

https://jzoj.net/senior/#main/show/6023

Problem
  • 给定 { a n } \{a_n\} {an},让你求一个字典序最小的排列 { p n } \{p_n\} {pn},使得 max ⁡ i = 1 n − 1 a p i ⨁ a p i + 1 \max_{i=1}^{n-1}a_{p_i}\bigoplus a_{p_{i+1}} maxi=1n1apiapi+1最小.
Data constraint
  • n ≤ 300000 n\le 300000 n300000
Solution
  • 首先注意到这个最大值一定是由一个最高位为 1 1 1的数异或一个最高位不为 1 1 1的数异或得来。所以按照这个最高位是否为 1 1 1分为两个集合 S 0 , S 1 S0,S1 S0,S1.

  • 之后我们可以用一个 T r i e Trie Trie简单的求出答案。但关键是怎么求排列。

  • 事实上,我们可以贪心的选择这个字典序最小的。然后我们把这两个集合之间可以连的边都连上。仔细思考后发现,如果某个集合 A A A的某个点 a a a连向另一个集合的边数小于当前 A A A连向 B B B还剩余的边数,那么我们就一定可以选 a a a,或者这个集合只有一个数时,我们也可以选 a a a。当然,如果可以的话,我们可以跳到集合 B B B去选。这里就预处理一些东西,用一个set维护即可。

  • 我打的很丑:

Code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define mem(a, b) memset(a, b, sizeof a)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define F(i, a, b) for (int i = (a); i <= (b); i ++)

const int N = 3e5 + 10, M = 40;

using namespace std;

int n, Ans, Mx, x, y, W, Sum[2], pt, tot, s0, s1, d0, cnt, st[N], S[N], Res[N], Ret[N], L[N], R[N];
int a[N], B[N], ANS[N], A[N], d[M], Son[2][M*N][2], ToT[2][M*N], Pre[2][M*N];
struct node {
	int x, num;
	bool operator < (const node a) const { return num < a.num; }
} T, t, b[N], S0[N], S1[N];
vector <int> V[N]; bool vis[N];
multiset <node> ST[2];

void Re(int &x) {
	char c = getchar(); x = 0; int t = 1;
	for (; !isdigit(c); c = getchar());
	for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = getchar()); x *= t;
}
void Init() {
	Re(n);
	F(i, 1, n) Re(a[i]);
}

void Doit() {
	do {
		if (Mx) F(i, 1, n) a[i] = a[i] - (1 << (Mx - 1));
		Mx = tot = 0;
		F(i, 1, n) Mx = max(Mx, a[i]);
		if (Mx == 0) {
			F(i, 1, n)
				printf("%d ", i);
			exit(0);
		}
		Mx = floor(log(Mx) / log(2)) + 1;
		F(i, 1, n) tot += floor(log(a[i]) / log(2)) + 1 == Mx;
	} while (tot == n);
}

void GetAns() {
	F(i, 1, n) b[i] = {i, a[i]};
	sort(b + 1, b + n + 1); b[0].num = b[1].num - 1; int cnt = 0;
	F(i, 1, n)
		cnt += (b[i].num != b[i - 1].num), B[b[i].x] = cnt;
	F(i, 1, n) {
		d0 = 0, mem(d, 0);
		for (int x = a[i]; x; d[++ d0] = x & 1, x >>= 1);
		if (d0 == Mx) {
			S1[++ s1] = {a[i], i}; int Rt = 0;
			for (int j = Mx; j; ToT[0][Rt = Son[0][Rt][d[j --]]] ++)
				if (!Son[0][Rt][d[j]])
					Son[0][Rt][d[j]] = ++ cnt;
			Pre[0][Rt] = B[i];
		}
		else {
			S0[++ s0] = {a[i], i}; int Rt = 0;
			for (int j = Mx; j; ToT[1][Rt = Son[1][Rt][d[j --]]] ++)
				if (!Son[1][Rt][d[j]])
					Son[1][Rt][d[j]] = ++ cnt;
			Pre[1][Rt] = B[i];
		}
	}
	Ans = 2e9;
	F(i, 1, s0) { d0 = 0, mem(d, 0);
		for (int x = S0[i].x; x; d[++ d0] = x & 1, x >>= 1);
		for (int j = Mx, Rt = 0; j ; j --)
			if (Son[0][Rt][d[j]])
				Rt = Son[0][Rt][d[j]];
			else
				Rt = Son[0][Rt][1 - d[j]], S[i] = S[i] + (1 << (j - 1));
		Ans = min(Ans, S[i]);
	}
}

void GetSet_And_SomeInit() {
	for (int x = Ans; x; A[++ A[0]] = x & 1, x >>= 1);
	F(i, 1, s0) { d0 = 0, mem(d, 0);
		for (int x = S0[i].x; x; d[++ d0] = x & 1, x >>= 1); int Rt = 0;
		for (int j = Mx; j && Rt || j == Mx; Rt = Son[0][Rt][A[j] ^ d[j --]]);
		Sum[0] += ToT[0][Rt];
		Res[B[S0[i].num]] = ToT[0][Rt];
		Ret[B[S0[i].num]] = Pre[0][Rt];
		ST[0].insert({Pre[0][Rt], S0[i].num});
	}
	F(i, 1, s1) { d0 = 0, mem(d, 0);
		for (int x = S1[i].x; x; d[++ d0] = x & 1, x >>= 1); int Rt = 0;
		for (int j = Mx; j && Rt || j == Mx; Rt = Son[1][Rt][A[j] ^ d[j --]]);
		Sum[1] += ToT[1][Rt];
		Res[B[S1[i].num]] = ToT[1][Rt];
		Ret[B[S1[i].num]] = Pre[1][Rt];
		ST[1].insert({Pre[1][Rt], S1[i].num});
	}
	F(i, 1, n)
		V[B[i]].push_back(i);
}

void Solve() {
	t = *ST[0].begin();
	if (Res[B[t.num]] == Sum[0] && ST[0].size() > 1 && ST[1].size())
		t = *++ST[0].begin();
	x = t.num, pt = t.x, T = t;
	t = *ST[1].begin();
	if (Res[B[t.num]] == Sum[1] && ST[1].size() > 1 && ST[0].size())
		t = *++ST[1].begin();
	y = t.num;
	if (y < x)
		x = y, pt = t.x, W = 1, T = t;
	ST[W].erase(ST[W].find(T));

	printf("%d ", x);
	F(i, 2, n) {
		vis[x] = 1;
		Sum[W] -= Res[B[T.num]];
		Sum[1-W] -= Res[B[T.num]];
		Res[Ret[B[x]]] --;
		t = *ST[W].begin();
		if (Res[B[t.num]] == Sum[W] && ST[W].size() > 1 && ST[1-W].size())
			t = *++ST[W].begin();
		T = t, x = t.num == 0 ? 1e9 : t.num;
		while (st[pt] < V[pt].size() && vis[V[pt][st[pt]]])
			st[pt]++;
		if (st[pt] < V[pt].size()) {
			y = V[pt][st[pt]];
			if (!(Res[B[y]] == Sum[1 - W] && ST[1 - W].size() > 1 && ST[W].size())) {
				t = *ST[1-W].lower_bound({0, y}), y = t.num == 0 ? 1e9 : t.num;
				if (y < x)
					x = y, W = 1 - W, T = t;
			}
		}
		pt = T.x;
		ST[W].erase(ST[W].find(T));
		printf("%d ", x);
	}	
}

int main() {
	freopen("roast.in", "r", stdin);
	freopen("roast.out", "w", stdout);

	Init();
	
	Doit();

	GetAns();
	
	GetSet_And_SomeInit();

	Solve();
}

你可能感兴趣的:(Trie,贪心,set)