• @hdu



    @description@

    给定一个稳定婚姻匹配问题,其中第 i 个男生与第 j 个女生之间的喜爱度为 ai xor bj。

    现在需要你求出所有稳定婚姻匹配中 ∑(ai xor bj) 的最大值。

    注:关于什么是稳定婚姻匹配。
    第 i 个男生对于第 j 个女生有一个喜爱度 Aij,第 j 个女生对第 i 个男生有一个喜爱度 Bij(在本题中 Aij = Bij)。
    一个稳定婚姻匹配应该满足:如果将 x 与 y 匹配,u 与 v 匹配,则不应该出现 x 对 v 的喜爱度 > x 对当前的 y 的喜爱度,且 v 对 x 的喜爱度 > v 对当前的 u 的喜爱度。

    Input
    第一行一个整数 T 表示数据组数。
    对于每组数据,开头一行包含一个整数 n (1≤n≤10^5)。
    第二行包含 n 个整数 a1, a2, ..., an (1≤ai≤10^9)。
    第三行包含 n 个整数 b1, b2, ..., bn (1≤bi≤10^9)。
    保证所有的 ai 互不相等,保证所有的 bi 互不相等。

    Output
    对于每组数据,输出一个整数表示 ∑(ai xor bj) 的最大值。如果无解输出 -1。

    Sample Input
    2
    4
    1 2 3 4
    1 2 3 4
    5
    10 20 30 40 50
    15 25 35 45 55
    Sample Output
    20
    289

    @solution@

    稳定婚姻问题挺有意思的 www(虽然这道题基本只需要用到它的定义)。
    稳定婚姻匹配不可能无解,所以无解输出 -1 是假的。

    假如 x 在 y 心中是最棒的,y 在 x 心中是最棒的,则 x 与 y 一定可以存在于稳定婚姻匹配中。
    且由于 ai, bi 互不相同,ax xor by 与其他的 ax xor bv, au xor ay 也一定不相同,且一定比它们大。
    由此,如果 x, y 与其他人 u, v 匹配,一定会出现不稳定的情况。则可以发现 x 与 y 存在于所有稳定婚姻匹配中。
    我们不断地找这样一对 (x, y),并把它们同时删去。

    怎么找呢?假如 x 与 y 的喜爱度 ax xor by 是全局最大的,则他们一定是互相认为最棒的。
    我们就不断地找 ax xor by 最大值及其对应的 x 与 y,然后删去 x 与 y 递归求解。
    可以发现一定有一个时刻所有人都删完了,而且中途不会出现多种选择。故这个题中稳定婚姻匹配是唯一的。

    那么问题就在于怎么不断地找 ax xor by 的最大值。
    两个值的 xor 最大值可以联想到 trie 树。两棵 trie 树找结点 xor 最大值,或许我们可以进行 trie 的 “合并”。

    对于两棵 trie 树 T1, T2,首先最高位尽量不同才能 xor 出来最大。
    所以首先应该尝试递归 T1->child[0] 与 T2->child[1],T1->child[1] 与 T2->child[0] 看能否找到最大值。
    如果有一棵树为空了,则匹配无法继续,直接停止递归并返回就好了。

    此时注意到这两次递归并不会互相影响,且这两次递归找出来的一定比 0, 0 匹配、1, 1 匹配大,所以可以同时进行。
    用不着像我们一开始的算法那样先找全局 xor 最大值,再找全局次大值……
    过后还要尝试递归 T1->child[0] 与 T2->child[0],T1->child[1] 与 T2->child[1],将匹配剩余的继续拿去匹配。

    复杂度?因为只有两棵树同时不为空时才会继续,那么复杂度 <= 将 n 对匹配插入 trie 树中的总复杂度。
    即复杂度为 O(nlogA),A 为值域。

    @accepted code@

    #include <cstdio>
    typedef long long ll;
    const int MAXN = 100000;
    int ch[2][2][35*MAXN + 5], cnt[2][35*MAXN + 5], ncnt[2];
    int newnode(int t) {
    	int p = (++ncnt[t]);
    	ch[t][0][p] = ch[t][1][p] = cnt[t][p] = 0;
    	return p;
    }
    void insert(int rt, int dep, const int &x, const int &t) {
    	cnt[t][rt]++;
    	if( dep == -1 ) return ;
    	int dir = (x >> dep) & 1;
    	if( !ch[t][dir][rt] )
    		ch[t][dir][rt] = newnode(t);
    	insert(ch[t][dir][rt], dep - 1, x, t);
    }
    ll ans; int n;
    void cal(int rt1, int rt2, int dep, int x) {
    	if( !cnt[0][rt1] || !cnt[1][rt2] )
    		return ;
    	if( dep == -1 ) {
    		ans += x, cnt[0][rt1]--, cnt[1][rt2]--;
    		return ;
    	}
    	cal(ch[0][0][rt1], ch[1][1][rt2], dep - 1, x|(1<<dep));
    	cal(ch[0][1][rt1], ch[1][0][rt2], dep - 1, x|(1<<dep));
    	cal(ch[0][0][rt1], ch[1][0][rt2], dep - 1, x);
    	cal(ch[0][1][rt1], ch[1][1][rt2], dep - 1, x);
    	cnt[0][rt1] = cnt[0][ch[0][0][rt1]] + cnt[0][ch[0][1][rt1]];
    	cnt[1][rt2] = cnt[1][ch[1][0][rt2]] + cnt[1][ch[1][1][rt2]];
    }
    void solve() {
    	scanf("%d", &n);
    	ncnt[0] = 0, newnode(0);
    	for(int i=1;i<=n;i++) {
    		int x; scanf("%d", &x);
    		insert(1, 30, x, 0);
    	}
    	ncnt[1] = 0, newnode(1);
    	for(int i=1;i<=n;i++) {
    		int x; scanf("%d", &x);
    		insert(1, 30, x, 1);
    	}
    	ans = 0; cal(1, 1, 30, 0);
    	printf("%lld
    ", ans);
    }
    int main() {
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    

    @details@

    话说这种新的两棵 trie 树搞合并方法。。。感觉还是比较新颖的?
    感觉以前都没有见过,又感觉好像很经典。。。

  • 相关阅读:
    python 递归一行实现字符串反转
    HABSE安装教程
    Target runtime Apache Tomcat v7.0 is not defined.
    论各种非人性化自动设置及关闭位置(持续更新中。。。)
    装饰者模式
    傻瓜式servlet监听器简单实例
    editplus代码格式化
    session,cookie机制
    servlet文件部署在tomcat上
    python学习笔记(一):作图
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11846595.html
Copyright © 2020-2023  润新知