zoukankan      html  css  js  c++  java
  • 【洛谷P3812】【模板】线性基

    题目

    题目链接:https://www.luogu.com.cn/problem/P3812
    给定 (n) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

    思路

    一个值域为 ([0,A]) 的序列的线性基长度为 (lceillog A ceil)。并且通过线性基内元素互相异或,可以得到原序列中每一个数字。
    具体的,我们设 (d)(a) 的线性基,那么 (d_i) 表示 (a) 中二进制最高位的 (1) 是第 (i) 位的一个数。假设我们要插入一个数 (x),那么我们就不断重复以下过程:

    1. 找到 (x) 的最高位的 (1),判断该位置 (d) 是否有数字。如果没有则赋值后直接 break。
    2. 否则设 (x) 最高位的 (1) 在位置 (i),那么让 (xgets x mathrm{xor} d_i)
    3. 如果 (x)(0) 则直接退出。

    因为每次操作二后 (x) 最高位的 (1) 都会变成 (0),所以复杂度是 (O(log x)) 的。
    这题要求异或和最大,我们就从高位到低位枚举,如果此时的答案 (res)(i) 位为 (0),那么就让 (resgets res mathrm{xor} d_i)。这样显然每次操作不会更劣。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=55;
    int n;
    ll x,d[N];
    bool flag;
    
    void insert(ll x)
    {
    	for (int i=N-1;i>=0;i--)
    		if (x&(1LL<<i))
    		{
    			if (!d[i]) { d[i]=x; return; }
    			x^=d[i];
    		}
    	flag=1;
    }
    
    ll getmax()
    {
    	ll ans=0;
    	for (int i=N-1;i>=0;i--)
    		if (!(ans&(1LL<<i))) ans^=d[i];
    	return ans;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%lld",&x);
    		insert(x);
    	}
    	printf("%lld",getmax());
    	return 0;
    }
    
  • 相关阅读:
    wikiquote
    zz 勵志貼,成功是努力加对的方向
    # 电纸书
    # 崔寶秋
    好的程序員
    深度学习引擎
    再见乱码:5分钟读懂MySQL字符集设置
    Linux基础:用tcpdump抓包
    Linux基础:文件查找find
    Linux基础:xargs命令
  • 原文地址:https://www.cnblogs.com/stoorz/p/14225576.html
Copyright © 2011-2022 走看看