HDU 6625 three arrays 杭电多校第五场 B 良心题解

HDU 6625 three arrays

题意

给出a数组和b数组
c数组为a[i]b[i]a[i] \oplus b[i]
重排列a,ba,b数组使得得到的c数组字典序最小

思路

最小的为a[i]与b[j]相互对应

首先,从b数组中能找到b[j]b[j]a[i]a[i] \oplus最小,我们称b[j]b[j]a[i]a[i]的对应点
如何查找对应点,我们可以用字典树(Trie树),将每个数字看成30位01字符串
我们将a[i]与b[i]与其对应点相连
在这里插入图片描述
显然,若a[i]与b[j]对应,并且b[j]与a[i]对应,那么a[i]与b[j]组合一定是当前可选的最小的
证明:显然,若存在a[i]b[z]<a[i]b[j]a[i]\oplus b[z]<a[i]\oplus b[j],a[i]a[i]b[z]b[z]对应,矛盾

必定存在a[i]a[i]b[j]b[j]相互对应

一个显然的事情是nn个点和nn个点两两连线,必定存在回路
证明:不太清楚的童鞋可以尝试构造一下无回路的情况,会发现最后一个点必定会形成回路

不会存在路径超过2的回路

显然路径为2的回路存在是合理的,即为相互对应
对于路径超过2的路径
如下图,若存在a[x]对应b[z],b[z]对应a[y],连接a[x]与b[z]的异或值大于b[z]与a[y]的异或值
a[2]b[1]<a[2]b[3]a[2]\oplus b[1]<a[2] \oplus b[3]在这里插入图片描述
下面我们简称一条边的值为一条边相连的两个值的异或
以下图为例,若存在回路,a[x]b[z]边大于a[y]b[z]\cdots\cdots,必定再次回到a[x]b[z]边造成矛盾
a[2]b[1]>a[2]b[3]>a[3]b[3]>a[3]b[1]>a[2]b[1]a[2]b[1]>a[2]b[3]>a[3]b[3]>a[3]b[1]>a[2]b[1],矛盾!在这里插入图片描述

具体实现

  • 若栈区为空,随便加一个点入栈
  • 若栈顶的对应点不在栈中,将其入栈
  • 若栈顶的对应点在栈中,必定为栈定第二个(最爱top元素的元素)
    我们以下图来模拟以下该过程
    1、将a[1]入栈(随便加一个)
    栈区:a[1]a[1]
    2、将b[2]入栈(将栈顶元素最爱的元素入栈)
    栈区:a[1],b[2]a[1],b[2]
    3、将a[3]入栈(将栈顶元素最爱的元素入栈)
    栈区:a[1],b[2],a[3]a[1],b[2],a[3]
    3、将b[3]入栈(将栈顶元素最爱的元素入栈)
    栈区:a[1],b[2],a[3],b[3]a[1],b[2],a[3],b[3]
    4、将a[3]和b[3]出栈(发现 栈顶元素 最爱的元素 即为 最爱栈顶元素 的元素,相亲相爱,牵手成功)
    栈区:a[1],b[2]a[1],b[2]
    在这里插入图片描述
    5、将a[2]入栈(b[2]移情别恋,爱上a[2])
    栈区:a[1],b[2],a[2]a[1],b[2],a[2]
    6、将b[1]入栈(将栈顶元素最爱的元素入栈)
    栈区:a[1],b[2],a[2],b[1]a[1],b[2],a[2],b[1]
    7、将a[2]和b[1]出栈(发现 栈顶元素 最爱的元素 即为 最爱栈顶元素 的元素,相亲相爱,牵手成功)
    栈区:a[1],b[2]a[1],b[2]在这里插入图片描述
    8、将a[1]和b[2]出栈(最爱你的人永远在你后面等你,单相思也会有结果qwq)
    栈区:空
    在这里插入图片描述
pii stack[maxn * 2];//first存大小,second存在哪个数组中,0表示a数组,1表示b数组
int top = 0;
int sum = 2 * n;
	while (sum) {
		if (!top) {			//栈区为空,随便从a数组中找一个数塞进去,估且就找个最爱1的吧
			stack[++top] = pii(T[0].find(1), 0);
			continue;
		}
		int symbol = T[stack[top].second ^ 1].find(stack[top].first);//symbol表示栈顶元素最爱元素
		if (top == 1 || stack[top - 1].first != symbol)		//栈顶元素最爱元素不在栈中
			stack[top + 1] = pii(symbol, stack[top].second ^ 1),	//入栈
			top++;
		else {
			res.push_back(stack[top].first ^ stack[top - 1].first);		//牵手成功双双出栈
			T[stack[top].second].insert(stack[top].first, -1);
			T[stack[top - 1].second].insert(stack[top - 1].first, -1);
			sum -= 2, top -= 2;
		}
	}

Trie树找最爱

插入与删除

一切尽在不言注释中

void insert(int str, int val) {				//val=1,表示插入一个数,val=-1表示删除一个数
		int position = root;				//初始化位置
		for (int i = 29; i >= 0; i--) {		//将每一位从高到低插入
			int symbol = (str >> i) & 1;	//提取二进制位
			if (!tree[position].son[symbol])//创建新节点
				tree[position].son[symbol] = ++num;
			position = tree[position].son[symbol];
			tree[position].mark += val;		//每一位都要记录有无,便于下方的查找
		}
	}

查找

int find(int str) {
		int position = root, w = 0;
		for (int i = 29; i >= 0; i--) {		//按二进制位从高到底查找
			int symbol = (str >> i) & 1;
			if (!tree[position].son[symbol] || !tree[tree[position].son[symbol]].mark) 
					symbol ^= 1;	//若该位symbol不存在,或者已被用光,那么选择另一个,0^1=1,1^1=0
			position = tree[position].son[symbol];			//迭代寻找
			w |= (symbol<< i);			//将该位存入答案
		}
		return w;
	}

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
#pragma warning (disable:4996)
typedef pair<int, int> pii;
const int maxn = 101000;
struct Tree {
	Tree() {
		son[0] = son[1] = mark = 0;
	}
	int mark;		//标记
	int son[2];	//此处只考虑小写字母
};
struct Trie {
	int root, num;		//根节点永久为0
	Tree tree[maxn * 33];
	Trie() {
		root = num = 0;
		memset(tree, 0, sizeof(0));
	}
	void init() {
		Tree st; 
		fill(tree, tree + num + 1, st);
		root = num = 0;
	}
	void insert(int str, int val) {
		int position = root;				//初始化位置
		for (int i = 29; i >= 0; i--) {
			int symbol = (str >> i) & 1;	//转化函数,视情况而定
			if (!tree[position].son[symbol])//创建新节点
				tree[position].son[symbol] = ++num;
			position = tree[position].son[symbol];
			tree[position].mark += val;
		}
	}
	int find(int str) {
		int position = root, w = 0;
		for (int i = 29; i >= 0; i--) {
			int symbol = (str >> i) & 1;
			if (!tree[position].son[symbol] || !tree[tree[position].son[symbol]].mark) symbol ^= 1;
			position = tree[position].son[symbol];//迭代寻找
			w |= (symbol<< i);
		}
		return w;
	}
}T[2];
int n;
void Read() {
	T[0].init();
	T[1].init();
	scanf("%d", &n);
	int x;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		T[0].insert(x, 1);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		T[1].insert(x, 1);
	}
}
pii stack[maxn * 2];
int top = 0;
vector<int> res;
void slove() {
	res.clear(); 
	top = 0;
	int sum = 2 * n;
	while (sum) {
		if (!top) {
			stack[++top] = pii(T[0].find(1), 0);
			continue;
		}
		int symbol = T[stack[top].second ^ 1].find(stack[top].first);
		if (top == 1 || stack[top - 1].first != symbol)
			stack[top + 1] = pii(symbol, stack[top].second ^ 1),
			top++;
		else {
			res.push_back(stack[top].first ^ stack[top - 1].first);
			T[stack[top].second].insert(stack[top].first, -1);
			T[stack[top - 1].second].insert(stack[top - 1].first, -1);
			sum -= 2, top -= 2;
		}
	}
}
int main() {
	int t; scanf("%d", &t);
	while (t--) {
		Read();
		slove();
		sort(res.begin(), res.end());
		for (int i = 0; i < res.size(); i++)
			printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' ');
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章