[洛谷P3857] [TJOI2008]彩灯

P3857 [TJOI2008]彩灯

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

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

问有多少种不同的状态。

做法

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

然后拉动开关就相当于给当前状态异或上一个数。

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

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

由于线性基异或得到的数必定没有重复的(根据基的定义,有且仅有一种方式得到一个数),那么选或不选线性基里的一个数就会导致得到不同的数。

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

注意溢出。

#include 
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;
}

你可能感兴趣的:([洛谷P3857] [TJOI2008]彩灯)