目录

  • 题意:
  • 解析
  • 原题描述

@(hdu 6625求两个序列异或最小值的排列)

题意:

\(T(100)\)组,每组两个长度为\(n(100000)\)的排列,你可以将\(a[]\)和\(b[]\)随机排列,可以得到\(c[i]=a[i]\)^\(b[i]\),求字典序最小的\(c[]\)。

解析

一个显然对的贪心做法:
针对本题

  • 每次两颗字典树同时往下走,如果都有\(0\)或者\(1\)这条路径,就随便同时走\(0\;or\;1\)这条路径,否则只能一个走\(0\),一个走\(1\)。这样复杂度是严格\(O(log)\)的,最后将得到的\(n\)个数字排序即为最后答案。
  • 这样为什么正确呢?
  • 如果当前两字典树都有\(0\)和\(1\)的路径,同时走\(0\)这条路得到数字肯定不能保证是当前能异或出来的最小值,但是可以肯定的是他一定是字典序最小的序列所包含的某个值。
  • 如果想单纯的求两个01字典树异或最小值,个人感觉还没有较好的复杂度的做法。

一个可以推广的正解:

  • 出题人\(dreamoon\)提供的正解:
  • 现在\(a[]\)中随便找一个数字\(x\),然后在\(b[]\)中相应找一个和\(x\)匹配异或最小的数字\(y\),再在\(a[]\)里面找一个和\(y\)匹配最小的数字\(z\),递归下去一定会找到一个大小为2的环
  • 把这个环这两个数字取出来,再回到上一个失配位置继续递归下去。
  • 这样得到的\(n\)个数字排序后即为最终答案。
  • 复杂度同样很科学并且这个思路适用性很广。

Code1

const int MXN = 1e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
int ar[MXN], br[MXN];
struct Trie {int tot;int nex[MXE][2], num[MXE], val[MXE];Trie(){nex[0][0] = nex[0][1] = -1;}void newnode() {++ tot;nex[tot][0] = nex[tot][1] = -1;}void inisert(int x) {int rt = 0;for(int i = 31, tmp; i >= 0; --i) {tmp = ((x>>i)&1);if(nex[rt][tmp] == -1) newnode(), nex[rt][tmp] = tot;rt = nex[rt][tmp];num[rt] ++;}val[rt] = x;}void del(int x) {int rt = 0;for(int i = 31, tmp; i >= 0; --i) {tmp = ((x>>i)&1);int lst = rt;rt = nex[rt][tmp];nex[lst][tmp] = -1;num[rt] = 0;}}
}cw[2];
bool check(int id, int rt, int tmp) {return cw[id].nex[rt][tmp] != -1 && cw[id].num[cw[id].nex[rt][tmp]] > 0;
}
int getans() {int rt1 = 0, rt2 = 0;for(int i = 31; i >= 0; --i) {if(check(0, rt1, 0) && check(1, rt2, 0)) {rt1 = cw[0].nex[rt1][0];rt2 = cw[1].nex[rt2][0];-- cw[0].num[rt1];-- cw[1].num[rt2];}else if(check(0, rt1, 1) && check(1, rt2, 1)) {rt1 = cw[0].nex[rt1][1];rt2 = cw[1].nex[rt2][1];-- cw[0].num[rt1];-- cw[1].num[rt2];}else if(check(0, rt1, 1) && check(1, rt2, 0)) {rt1 = cw[0].nex[rt1][1];rt2 = cw[1].nex[rt2][0];-- cw[0].num[rt1];-- cw[1].num[rt2];}else if(check(0, rt1, 0) && check(1, rt2, 1)) {rt1 = cw[0].nex[rt1][0];rt2 = cw[1].nex[rt2][1];-- cw[0].num[rt1];-- cw[1].num[rt2];}}return cw[0].val[rt1] ^ cw[1].val[rt2];
}
int main() {
#ifndef ONLINE_JUDGEfreopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endifint tim = read();while(tim --) {n = read();cw[0].tot = cw[1].tot = 0;for(int i = 1; i <= n; ++i) ar[i] = read(), cw[0].inisert(ar[i]);for(int i = 1; i <= n; ++i) br[i] = read(), cw[1].inisert(br[i]);vector<int> vs;for(int i = 1; i <= n; ++i) vs.eb(getans());sort(all(vs));for(int i = 0; i < SZ(vs); ++i) printf("%d%c", vs[i], " \n"[i == SZ(vs) - 1]);for(int i = 1; i <= n; ++i) cw[0].del(ar[i]), cw[1].del(br[i]);}return 0;
}

Code2

const int MXN = 1e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
int ar[MXN], br[MXN];
struct Trie {int tot;int nex[MXE][2], num[MXE], val[MXE];Trie(){nex[0][0] = nex[0][1] = -1;}void newnode() {++ tot;nex[tot][0] = nex[tot][1] = -1;}void inisert(int x) {int rt = 0;for(int i = 30, tmp; i >= 0; --i) {tmp = ((x>>i)&1);if(nex[rt][tmp] == -1) newnode(), nex[rt][tmp] = tot;rt = nex[rt][tmp];num[rt] ++;}val[rt] = x;}int query(int x) {int rt = 0;for(int i = 30, tmp; i >= 0; --i) {tmp = ((x>>i)&1);if(nex[rt][tmp] != -1 && num[nex[rt][tmp]]) rt = nex[rt][tmp];else rt = nex[rt][!tmp];}return val[rt];}int find() {int rt = 0;for(int i = 30, tmp; i >= 0; --i) {if(nex[rt][0] != -1 && num[nex[rt][0]]) rt = nex[rt][0];else if(nex[rt][1] != -1 && num[nex[rt][1]]) rt = nex[rt][1];}if(rt == 0) return -1;return val[rt];}void del() {for(int i = 0; i <= tot + 1; ++i) num[i] = 0, clr(nex[i], -1);tot = 0;}void sub(int x) {int rt = 0;for(int i = 30, tmp; i >= 0; --i) {tmp = ((x>>i)&1);rt = nex[rt][tmp];num[rt] --;}}
}cw[2];
/** 这种做法不能保证每次求出来的异或最小值都是单调递增的,但是将n次得到的值排序后一定是正确答案* 如果想单纯的求两个01字典树异或最小值,个人感觉还没有较好的复杂度的做法。* 关于本题,还有一个出题人提供适用性更加广泛的正解:* 现在a中随便找一个数字,然后在b中找一个和他匹配最小的数字,再在a里面找一个和上个数匹配最小的数字,递归下去一定会找到一个大小为2的环* 把这个环取出来,在回到上一个位置继续递归下去。得到的n个数字排序即为最终答案。* */
vector<int> vs;
int dfs(int id, int x, int lst) {int tmp = cw[!id].query(x);if(tmp == lst) {vs.eb(tmp ^ x);cw[id].sub(x);cw[!id].sub(tmp);return id;}int ret = dfs(!id, tmp, x);if(ret != id) return ret;
}
int main() {
#ifndef ONLINE_JUDGEfreopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endifint tim = read();while(tim --) {n = read();for(int i = 1; i <= n; ++i) ar[i] = read(), cw[0].inisert(ar[i]);for(int i = 1; i <= n; ++i) br[i] = read(), cw[1].inisert(br[i]);vs.clear();while(1) {int tmp = cw[0].find();if(tmp == -1) break;dfs(1, tmp, -1);}sort(all(vs));for(int i = 0; i < SZ(vs); ++i) printf("%d%c", vs[i], " \n"[i == SZ(vs) - 1]);cw[0].del(), cw[1].del();}return 0;
}

原题描述

转载于:https://www.cnblogs.com/Cwolf9/p/11330789.html

HDU 6625 three arrays 求两个序列异或最小值的排列(一个可以推广的正解相关推荐

  1. 如何求两个序列的相似度

    引入 衡量两个序列的相似度,可以用马氏距离,欧氏距离等距离公式来度量. 但对两个字符串,比如kitten与sitting的相似度是多少? 如果是两个等长字符串,也可以用one-hot对每个字母编码,然 ...

  2. 新水果取名(动态规划,求两个子序列的最短原序列)

    题目内容: 两种水果杂交出一种新水果,现在给新水果取名,要求这个名字中包含以前两种水果的字母,且名字尽量短,即:以前的水果名字arr1.arr2是新水果名arr12的子序列,使用动态规划的思想设计算法 ...

  3. 2019 杭电多校 HDU - 6625 three arrays 字典树+贪心

    题目链接:https://cn.vjudge.net/problem/HDU-6625 题意:a和b两个数组n个数,数字任意组合异或,求得到c数组的字典序最小 题解:对于两个数组从高位到低位建立两个字 ...

  4. HDU - 6625 three arrays (Trie+dfs)

    题目链接 题意 两个数组A.BA.BA.B,数组的顺序随意,你需要得到一个数组CCC,满足C[i]=A[i]xorB[i]C[i] = A[i] xor B[i]C[i]=A[i]xorB[i]且字典 ...

  5. 求两个数中的最大值最小值算法

    给定两个数,用数学运算求出它们两中的最大的数和最小的数,设这两个数为a,b,最大值为max,最小值为min,则有 max = ((a+b)+|a-b|) / 2 ; min = ((a+b)-|a-b ...

  6. 求两个整数数组乘积最小值

    输入: 三行: 第一行:n,表示数组的个数 第二行:数组a各个元素的值 第三行:数组b各个元素的值 输出: 式子:a[0]*b[0]+a[1]*b[1]+...a[n-1]*b[n-1];的最小值 其 ...

  7. 求两个数的最大公约数的3种办法

    2019独角兽企业重金招聘Python工程师标准>>> import org.junit.Test;import java.util.ArrayList; import java.u ...

  8. C语言试题七十三之请编写函数求两个数的最大公约数

    1. 题目 编写函数:求两个数的最大公约数. 如果有一个自然数a能被自然数b整除,则称a为b的倍数,b为a的约数.几个自然数公有的约数,叫做这几个自然数的公约数.公约数中最大的一个公约数,称为这几个自 ...

  9. C#--第2周实验--任务五--编写一个控制台应用--求两个整数的最小公倍数与最大公约数

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All rights reserved. * 文件名称:输入两个整数, ...

最新文章

  1. php表格之间设置间隔,html表格如何设置间距
  2. Linux基础知识--进程管理与计划任务
  3. 常用的web安全处理
  4. 流传在程序员中的传说,你知道几个?
  5. Flex 扩展combobox 实现复选功能
  6. Anaconda的安装与基本使用
  7. matlab的控制系统仿真,MATLAB控制系统仿真教程
  8. 多个Excel文件合并到一个Excel文件的多个工作表(Sheet)里
  9. 【ELIXIR】简单说下elixir的历史
  10. php allow origin,Allow-Control-Allow-Origin:谷歌跨域扩展插件
  11. Guns二次开发目录
  12. Internet Download Manager(V6.37版本IDM)免费序列号密钥激活版使用过程中的一些常见问题
  13. jsonObject.getString()解析任意字段均可强转为string
  14. 基于hadoop下的使用map reduce分布式系统的高考高频词汇统计
  15. ABP框架系列之三十:(Javascript-API-Javascript-API)
  16. 使用开源软件XWIKI搭建公司内部WIKI系统
  17. unity 如何切换输入系统(Input System)
  18. 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格
  19. E01 GBase 8a MPP Cluster V95 安装和卸载
  20. Tomcat安装与使用

热门文章

  1. 快速排序 - python版超详细讲解
  2. 数仓系列 | 深入解读 Flink 资源管理机制
  3. eq linux_《Linux设备驱动程序》(十二)——时间操作(一)
  4. 【复杂网络分析】motif、cluster、clique、community 的介绍和比较
  5. 群组密钥交换的新方法研究与分析【会议】
  6. 图的所有顶点间的最短路径(Floyd算法)
  7. Java 8 实战学习笔记
  8. Namomo Spring Camp Div2 Week1 - 第一次打卡
  9. QT 012 [深入] Qt setupUi函数的原理和实现
  10. 类型全部为string_TypeScript 高级类型总结(含代码案例)