洛谷3857 [TJOI2008]彩灯

线性基入门:

给你一个数列N个数字,构造一个线性基出来。对于原数列任意一个数字,都可通过选取线性基中若干数字XOR运算得到。

两个公式:

1:如果A^B^C=0,则A^B=C,如果一个数字C没有加入到线性基中,说明已存在A,B,且A^B=C

2:如果A^B=C,则A^C=B.

构造程序如下:

#include<bits/stdc++.h>
using namespace std;
int N,M,cnt,ans;
long long P[128];
char s[128];
void insert(long long x)
{
    for(int i=62;i>=0;i--)
        if((x>>i)&1)
		{
            if(!P[i])
			{
				P[i]=x;
				break;
			}
            else 
			     x^=P[i];
        }
}
int main()
{
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
	{   
		int aa;
		cin>>aa;
        insert(aa);
    }
    for(int i=0;i<=62;i++)
	if(P[i])
	cout<<P[i]<<endl;
    return 0;
}

 运行过程:

当加入9(1001)的时候,直接加入
当加入8(1000)的时候,其最高位那个1,所在的位置已有9加入了,
所以8^9=1,加入之0001
当加入7(0111)的时候,直接加入。
当加入6(0110)的时候,直接加入,其最高位那个1,所在的位置已有7加入了,
所以6^7=0001,然后1又已在线性基中了,xor后得到0,不加入。
当加入5(0101)的时候,其最高位那个1,所在的位置已有7加入了
所以5^7=2,加入之0010
当加入4后,4^5^1=0,不加入
当加入3后,3^2^1=0,不加入

当加入2后,不加入

当加入1后,不加入
..................
最终形成的线性基为9,1,7,2.总个数是最小的,且这些数转成4位2进制后
其最高位如果为1的,将是各个数字中是唯一的。

注意构造出来的线性基并不是唯一的。

 对于上面这个数列,线性基为(1,2,4,8)也是可以的。

 

 

已知一组彩灯是由一排N个独立的灯泡构成的,并且有M个开关控制它们。从数学的角度看,这一排彩灯的任何一个彩灯只有亮与不亮两个状态,所以共有2N个样式。由于技术上的问题,Peter设计的每个开关控制的彩灯没有什么规律,当一个开关被按下的时候,它会把所有它控制的彩灯改变状态(即亮变成不亮,不亮变成亮)。假如告诉你他设计的每个开关所控制的彩灯范围,你能否帮他计算出这些彩灯有多少种样式可以展示给他的女朋友?

注: 开始时所有彩灯都是不亮的状态。

输入输出格式

输入格式:

每组测试数据第一行为两个整数N和M,用空格隔开。紧接着是有M行,每行都是一个长度为N的字符串,表示一个开关控制彩灯的范围(N盏灯),如果第i个字母是大写字母’O’,则表示这个开关控制第i盏灯,如果第i个字母是大写字母’X’,则表示这个开关不控制此灯。

输出格式:

输出这些开关和彩灯可以变换出来的样式数目。
由于这个值可能会很大,请求出它对于整数2008的余数。

输入输出样例

输入样例#1:
2 3
OO
XO
OX
输出样例#1:
4
说明

可见样例中第一个开关控制了所有的彩灯,而后两个开关分别控制了第一个和第二个彩灯,这样我们可以只用后两个开关控制彩灯,可以变换出来所有的22个状态。

 本题可以翻译成给出M个N位二进制数 , 取若干个异或,求有多少种结果。

本质就是求出线性基的个数x,结果即为2^x

线性基就是把原有的集合用一个新的集合替代之,新的集合里面数相互异或可以得出原有集合的数相互异或的答案。

搞出线性基的性质 : a 和 b 的所有xor 结果 等同于 a和 a xor b的所有结果。

#include<bits/stdc++.h>
using namespace std;
int N,M,cnt,ans;
long long P[128];
char s[128];
void insert(long long x)
{
    for(int i=62;i>=0;i--)
        if((x>>i)&1){
            if(!P[i]){P[i]=x;break;}
            else x^=P[i];
        }
}
int main()
{
    scanf("%d%d",&N,&M);
    for(int i=1;i<=M;i++){
        scanf("%s",s);
        long long a=0;
        for(int j=0;j<N;j++)
            if(s[j]=='O')a^=(1ll<<j);
        insert(a);
    }
    for(int i=0;i<=62;i++)if(P[i])ans++;
    printf("%lld",(1ll<<ans)%2008);
    return 0;
}

  

posted @ 2020-04-23 16:36  我微笑不代表我快乐  阅读(133)  评论(0编辑  收藏  举报