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' : ' ');
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章