15年湖南ACM省赛 Internet of Lights and Switches(异或运算+状压+二分)

本文探讨了一道关于物联网(IoT)中控制灯泡开关的算法问题,旨在通过使用特定数量的连续开关来关闭所有初始打开的灯泡。文章详细介绍了如何利用二进制表示和异或操作来寻找解决方案,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Internet of Lights and Switches
Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format:

Description

You are a fan of "Internet of Things"(IoT, 物联网), so you build a nice Internet of Lights and Switches in your huge mansion. Formally, there are lights and switches, each switch controls one or more lights, i.e. pressing that switch flips the status of those lights (on->off, off->on).


Initially, all the lights are on. Your task is to count the number of ways to turn off all the lights by pressing someconsecutive switches. Each switch should not be pressed more than once. There is only one restriction: the number of switches you pressed should be between a and b (inclusive).





Input

There will be at most 20 test cases. Each test case begins with a line containing four integers n, m, a, b (2<=n<=50, 1<=a<=b<=m<=300000). Each of the following m lines contains a 01 string of length n. The i-th character is 1 if and only if that switch controls the i-th light. The size of the whole input file does not exceed 8MB.

Output

For each test case, print the case number, and the number of ways to turn off all the lights.

Sample Input

2 4 1 4
01

10

11

00 
2 4 3 3
01 
10

11

00
6 3 1 3
101001
010110
101001 

Sample Output

Case 1: 3
Case 2: 0
Case 3: 2

题意:有n个灯泡,一开始全是开着的,现在给你m个开关用一个长为n的字符串表示,第i个字符为0表示开关不能控制灯泡i,第i个字符
为1表示开关能控制灯泡i,每个开关只能使用一次,现在要求只能按长度>=a且<=b连续相邻的开关,问有多少种方法关掉所有灯泡
题解:用二进制表示开关的开关状态:1是开,0是关.初始状态就是sum=(1<<n)-1,我们可以想到,如果用异或前缀和sz[]表示从第1个
开关按到第n个开关时,灯泡的状态,那么如果要按区间[st,ed]内的开关来关掉所有灯泡,那么按下第ed个开关时的状态一定要等于
按下第st-1个开关时的状态^sum,因为这样按下[st,ed]内的开关可以使所有灯泡的暗亮状态反转,自然也可以让初始状态反转成0,
于是我们可以用map储存状态t和每次出现状态t时候按下的开关的编号,设按下第i个开关的状态是t,二分查找区间[i-a+1,i-b+1]
内状态为t^sum的个数,全部加起来就是方法数

#include<cstdio>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
int n, m, a, b;
LL sz[333333];    //sz储存异或前缀和
int main() {
    int T = 1; char s[100];
    //  freopen("in.txt", "r", stdin);
    while(~scanf("%d%d%d%d", &n, &m, &a, &b)) {
        LL sum = (1ll << n) - 1;
        map<LL, vector<int> > my;    //first储存当前开关状态,second储存这个状态分别出现在第几个开关
        my[sum].push_back(0);
        sz[0] = sum;
        for(int i = 1; i <= m; i++) {
            scanf("%s", s);
            LL cnt = 0;
            for(int j = 0; j < n; j++) if(s[j] == '1') cnt += (1ll << j);
            sz[i] = cnt ^ sz[i - 1];
            my[sz[i]].push_back(i);
        }
        int cnt = 0;
        for(int i = a; i <= m; i++) {
            LL t = sum ^ sz[i];
            if(!my[t].size()) continue;
            int l = lower_bound(my[t].begin(), my[t].end(), i - b) - my[t].begin();//查找出现状态t的最开始的位置
            int r = upper_bound(my[t].begin(), my[t].end(), i - a) - my[t].begin();//查找出现状态t的最后位置+1
            int tt = r - l; //出现状态t的次数
            cnt += tt;
        }
        printf("Case %d: %d\n", T++, cnt);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值