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

目录

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

@(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_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    int 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_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    int 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

时间: 2024-11-05 20:39:43

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

阿里笔试题:求两个子序列的最大连续子序列

原题例如以下: 给定一个query和一个text,均由小写字母组成.要求在text中找出以相同的顺序连续出如今query中的最长连续字母序列的长度.比如.query为 "acbac",text为"acaccbabb",那么text中的"cba"为最长的连续出如今query中的字母序列,因此.返回结果应该为其长度3.请注意程序效率. 解题方法一: 和字符串匹配一样(http://blog.csdn.net/sinat_24520925/articl

动态规划求两个序列的最长公共子序列

摘录:http://blog.chinaunix.net/uid-26548237-id-3374211.html 1.序列str1和序列str2 ·长度分别为m和n: ·创建1个二维数组L[m.n]: ·初始化L数组内容为0 ·m和n分别从0开始,m++,n++循环: - 如果str1[m] == str2[n],则L[m,n] = L[m - 1, n -1] + 1: - 如果str1[m] != str2[n],则L[m,n] = max{L[m,n - 1],L[m - 1, n]}

位运算求两数的最大值与最小值

1.最小值:(y^(x^y)&-(x<y)). 2.最大值:(y^(x^y)&(x<y-1))或(x^(x^y)&-(x<y)).

hdu 3264 Open-air shopping malls 求两圆相交

对每个圆二分半径寻找可行的最小半径,然后取最小的一个半径. 对于两圆相交就只要求到两个扇形,然后减去两个全等三角形就行了. #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; #define pi acos(-1.0) #define eps 1e-8 #define maxn 50 int n; struct point{

Fermat Point in Quadrangle(hdu 3694 求两直线交点

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3694 画几个图应该就可以知道凸四边形就是对角线交点 凹四边形就是凹进去的那个点 so 只要枚举四个点以及对角线交点 找个minn就可以 求两直线交点模板: double cross(double x1,double y1,double x2,double y2) { return x1*y2-x2*y1; } bool getcross(double x1,double y1,double x2,d

HDU 2639 01背包求第k大

Bone Collector II Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3718    Accepted Submission(s): 1903 Problem Description The title of this problem is familiar,isn't it?yeah,if you had took par

求两个字符串最长公共子串

一.问题描述: 最长公共子串 (LCS-Longest Common Substring) LCS问题就是求两个字符串最长公共子串的问题.比如输入两个字符串"ilovechina"和“chinabest”的最长公共字符串有"china",它们的长度是5. 二.解法 解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0.然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.如下图: i   l   o  v  e  

hdu1710-Binary Tree Traversals (由二叉树的先序序列和中序序列求后序序列)

http://acm.hdu.edu.cn/showproblem.php?pid=1710 Binary Tree Traversals Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4210    Accepted Submission(s): 1908 Problem Description A binary tree is a

数据结构——算法之(032)(求两个串中的第一个最长子串)

[申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出. 联系邮箱:[email protected]] 题目: 求两个串中的第一个最长子串(神州数码曾经试题).如"abractyeyt","dgdsaeactyey"的最大子串为"actyey". 题目分析: 1.这里仅仅是实现了简单的字符串算法(最大支持字符串长度64),主要是展示算法思想 2.思路是把2个字符串每一个字符的匹配关系,映射到一张二维数组表中,匹配写1,非匹配写0 算法实现