[洛谷P3857] [TJOI2008]彩灯

P3857 [TJOI2008]彩灯

n个灯,每一个灯只有开关两种状态,开始都关着。ios

m个开关,每一个开关对应若干个灯,拉动一下就会使该开关控制的全部灯改变状态。spa

问有多少种不一样的状态。code

作法

考虑把每一个灯抽象成一个二进制位,开是1,关是0,那么当前状态就是一个二进制数。ci

而后拉动开关就至关于给当前状态异或上一个数。get

因而问题转化为求一些数能xor出多少不一样的数。(由于一开始是0,异或0等于不异或)io

考虑创建一个线性基。既然原序列xor能获得的集合与线性基xor能获得的集合相同,那么其大小也是相同的。class

因为线性基异或获得的数一定没有重复的(根据基的定义,有且仅有一种方式获得一个数),那么选或不选线性基里的一个数就会致使获得不一样的数。stream

而每一个数有选和不选两种状态,因此答案是\(2^{size}\)\(size\)是线性基内元素个数,即线性基大小)二进制

注意溢出。集合

#include <iostream>
using namespace std;
char read()
{
    char c;
    do
    {
        c = getchar();
    } while (c == ' ' || c == '\n' || c == '\r');
    return c;
}
typedef long long type;
const int W = 51;
type basis[W + 1];
int siz;
void ins(type x)
{
    for (int i = W; i >= 1; i--)
    {
        if (x >> (i - 1))
        {
            if (basis[i] == 0)
            {
                basis[i] = x;
                siz++;
                return;
            }
            x ^= basis[i];
        }
    }
}
int w, n;
int main()
{
    cin >> w >> n;
    for (int i = 1; i <= n; i++)
    {
        type x = 0;
        for (int j = 1; j <= w; j++)
        {
            char c;
            c = read();
            if (c == 'O')
                x += 1ll << (j - 1);
        }
        ins(x);
    }
    cout << (1ll << siz) % 2008 << endl;
}
相关文章
相关标签/搜索