zoukankan      html  css  js  c++  java
  • 线性基

    对于一个序列,我们要求它异或的最大/最小值,这是线性基应用的经典情况。
    一个序列 (a) 的线性基,指一个等效数组 (p) ,这两个数组元素异或的值域相同
    设一个序列 (a) 的线性基为 (p)(p_i) 表示出现 (1) 的最高位在第 (i) 位上的数字。
    显然,这个数组的大小仅有 (logn) 所以就能处理更多问题。

    • 构造方法
      由于 (p) 是一个等效数组,也就是说,p的元素没必要都在 (a) 中出现。只需要保证可以通过异或操作拼出 (a) 中的元素就可以
      于是对于加入一个数 (x) 的操作:
      从高到低检查x在二进制下的每一位,若x当前位置为1 则检查 (p_i)
      (p_i)为1 则将x异或(p_i),继续寻找
      (p_i)为0 则(p_i=x),插入成功
      (x) 值变成0,则证明 (x) 已经可以用线性基中的数字表示,插入失败。
      当查询最大值时,我们只需要对于每一位进行贪心,如果异或上之后会使答案变大就进行异或操作。
      至于最小值,一般来说就是线性基里的最小元素,因为插入这个元素的时候我们总是尽量让它的高位为 (0) 才来插入这一位。但是为什么是“一般”呢?因为有可能会有出现 (0) ,得要在插入的时候记下个标记来特判才行。
    • 为什么是这样?
      对于每一位,我们都努力给它匹配一个唯一的数字使得这一位能异或出来1。这样就很巧妙的把异或问题变成了贪心。

    例题 P3812 【模板】线性基

    求能异或出来的最大值。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<cstring>
    #include<stack>
    #include<queue>
    #include<vector>
    #include<bitset>
    #include<set>
    #define ll long long
    using namespace std;
    const int inf = 0x7fffffff;
    int n;
    ll p[60];
    bool Add(ll x)
    {
    	for(int i=60;i>=0;i--)
    	{
    		if(x&(1ll<<i))
    		{
    			if(!p[i])
    			{
    				p[i]=x;
    				return 1;
    			}else x^=p[i];
    		}
    		if(x==0)return 0;
    	}
    }
    ll Query()
    {
    	ll ans=0;
    	for(int i=60;i>=0;i--)
    	{
    		if((ans^p[i])>ans)ans^=p[i];
    	}
    	return ans;
    }
    signed main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		ll x;
    		scanf("%lld",&x);
    		Add(x);
    	}
    	printf("%lld
    ",Query());
    	return 0;
    }
    

    例题 P3857 [TJOI2008]彩灯
    每个开关控制了一些特定的灯
    求一共能搞出多少种不同形态的灯。

    发现开关一个开关其实就相当于异或操作
    于是问题变成了给你一些数字 问互相异或能找出多少个不同的数字。
    使用线性基 因为线性基内的数字在异或下的值域和原数组相同 所以线性基每个数都有选或不选两种方案 答案数量即为 (2^{size})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<cstring>
    #include<stack>
    #include<queue>
    #include<vector>
    #include<bitset>
    #include<set>
    #define ll long long
    using namespace std;
    const int inf = 0x7fffffff;
    int n,m;
    #define mod 2008
    ll ksm(ll a,ll b)
    {
    	ll base=a,res=1;
    	while(b)
    	{
    		if(b&1)res=res*base%mod;
    		base=base*base%mod;
    		b>>=1;
    	}
    	return res;
    }
    ll p[60],siz=0;
    bool Add(ll x)
    {
    	for(int i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(!p[i])
    			{
    				p[i]=x;
    				siz++;
    				return 1;
    			}else x^=p[i];
    		}
    		if(x==0)return 0;
    	}
    	return 0;
    }
    char ch[88];
    signed main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%s",ch+1);
    		ll now=0;
    		for(int j=1;j<=n;j++)
    		{
    			if(ch[j]=='O')now++;
    			now<<=1;
    		}
    //		cout<<now/2<<endl;
    		Add(now>>1);
    	}
    //	cout<<siz<<endl;
    	printf("%lld
    ",(ksm(2,siz))%mod);
    	return 0;
    }
    
    
  • 相关阅读:
    【转】PHP中文乱码的常见解决方法总结(GOOD)
    【转】Apache的安装与配置
    【转】PHP Notice: Undefined index: ... 问题的解决方法
    【转】方程的解数(哈希表的应用)
    【转】PHP Notice: undefined index 完美解决方法
    【转】如何修改IIS的默认端口号
    Apache配置(转载)
    BZOJ 1014 Splay+hash
    BZOJ 1045 环形均分纸牌
    BZOJ 1008 快速幂
  • 原文地址:https://www.cnblogs.com/lzy-blog/p/15433580.html
Copyright © 2011-2022 走看看