字典树_异或和

2018-03-16
异或的性质

  1. a ⊕ a = 0
  2. a ⊕ b = b ⊕ a
  3. a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;
  4. d = a ⊕ b ⊕ c 可以推出 a = d ⊕ b ⊕ c.
  5. a ⊕ b ⊕ a = b.
    6.若x是二进制数0101,y是二进制数1011;
    则x⊕y=1110

有上述性质,对于区间异或和要知道如下性质:
XOR[l,r] = XOR[1,l-1] ^ XOR[1,r]

异或的知识还有很多有趣的解释和应用,详情可以移步知乎。

(CodeForces - 948D)Perfect Security
题意:先给你一串数字a[n],然后再给你一串数字b[n],让你任意排列b[n],使得a[i]^b[i]的值的输出字典序最小。
思想:想办法让一个数字k异或后的结果最小,其实就是想办法让该数字二进制高位变为0,而由上面异或的性质可以知道,其实就是在另一串数字中找到一个数字d,使得数字d的二进制与数字k的二进制在高位上尽可能多得相同,于是就有了两个方法。
方法一:暴力寻找(??)

for(ll i=1<<30;i;i>>=1){
            ans+=(i&x);
            multiset::iterator it=S.lower_bound(ans);
            if(it==S.end() || *it>=ans+i){
                ans^=i;
            }
        }
        S.erase(S.find(ans));

从x的高位找,如果a[n]中的数字最大值还不能达到高位,或者大于x的最小数字在二进制中甚至比x还高一位,那么异或取消。
还有一个有趣的一点就是:怎么确认我找到的ans一定是在b[n]呢——其实关键点就在lower_bound这里,举个例子就能理解。

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define lowbit(x) (x&(-x))
#define ll long long
#define PI acos(-1)
#define FILEIN freopen("in.txt","r",stdin)
#define FILEOUT freopen("out.txt","w",stdout)
#define CLR(x) memset(x,0,sizeof(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define debug printf("!!");

const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3fll;
using namespace std;

//const double eps = 1e-6;
const ll mod = 1e9+7;
const int maxn = 300000+100;
const int maxe = 1e6+100;

using namespace std;

multisetS;
ll a[maxn];

void Solve()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i>=1){
            ans+=(i&x);
            multiset::iterator it=S.lower_bound(ans);
            if(it==S.end() || *it>=ans+i){
                ans^=i;
            }
        }
        S.erase(S.find(ans));
        printf("%lld",x^ans);
    }
}

int main()
{
    //FILEIN
    //FILEOUT
    //std::ios::sync_with_stdio(false);
    int Case=1,cases;
    //scanf("%d", &Case); cases=Case;
    while(Case--){
        //printf("Case #%d:",cases-Case);
        Solve();
    }
    return 0;
}

方法二:常规01字典树。O(n*32(字典树深度))

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define lowbit(x) (x&(-x))
#define ll long long
#define PI acos(-1)
#define FILEIN freopen("in.txt","r",stdin)
#define FILEOUT freopen("out.txt","w",stdout)
#define CLR(x) memset(x,0,sizeof(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define debug printf("!!");

const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3fll;
using namespace std;

//const double eps = 1e-6;
const ll mod = 1e9+7;
const int maxn = 9e6+100;
const int maxe = 1e6+100;

using namespace std;

struct node{
    int nex[2],num;
}trie[maxn];

int tot=1;
int num[maxn],a[maxn],b[maxn];

void Insert(int x,int id){
    int rt=0;
    for(int i=30;i>=0;i--){
        int t=((x&(1<=0;i--){
        int t=((x&(1<=0;i--){
        int t=((x&(1<

你可能感兴趣的:(字典树_异或和)