1784: Internet of Lights and Switches
Submit Page Summary 5 Sec 128 Mb 196 19
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 n lights and m 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 some consecutive 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
Hint
Source
挺有意思的一道题目……
就是有m个开关,有n盏灯,然后每个开关可以控制多个灯。开始时灯都是卡着的,你可以连续的摁一段区间的开关,使得灯的状态发生改变,问你有多少种摁法可以使得最后的所有灯都灭,还有可以摁的开关个数一定要在规定区间以内。换句话来说,就是有多少个区间的异或和是n个1。
这题比较好的地方是已经比较明显的提示了你用状态压缩。然后其实我一开始想了一会儿没有思路,完全就是因为刚刚那个异或和的影响……如果你考虑真的求和的话,不论怎么考虑时间复杂度都不能满足。于是我们考虑逆向思维,我不考虑和,我考虑差,即对于当前状态,我考虑它还要异或上什么状态才能达到最终状态,然后这种状态总共有多少种。于是我们就可以维护一个当前状态,和当前区间的状态情况。
我们从第一个数字开始考虑,初始时状态为0,由于是要求连续的区间,我们先求出前缀异或和s[i],s[1]=a1、s[2]=a1^a2、s[3]=a1^a2^a3……然后把每一个出现的状态以及其数量存在map中。现在map中相当于存的是以第一个数为左端点的区间的异或和,此时贡献就是状态为n个1的状态数量,为了方便我们设final=n个1。然后到下一位,相应的要知道所有以第二个数为左端点的区间异或和的状态情况。我们刚刚说了map存的是以第一个数为左端点的区间,那么如何维护呢?我们知道a^b^b=a,所以为了消除第一个数对区间和的影响,我们可以对所有数字异或第一个数字,但实际上对区间操作不太现实,所以我们改变当前状态,把当前状态异或上第一个数字。如此之后,我们的贡献就是状态final^a1的数量。一直往后都是一样,维护这样一个状态情况。
我们之前是没有考虑区间限制情况,那么考虑了之后呢?其实无伤大雅,无非就是map中维护的只是一段的异或前缀和,而不是所有。然后随着左端点的移动,里面的状态种类以及数量要做相应的变化。具体维护方法以及细节见代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#define LL long long
#define N 300010
using namespace std;
int n,m,L,R,T_T=0;
map<LL,LL> mp;
LL a[N],s[N],p[100];
char ch[100];
void init()
{
p[0]=1;
for(int i=1;i<=51;i++) p[i]=p[i-1]*2;
}
int main()
{
init();
while(~scanf("%d%d%d%d",&n,&m,&L,&R))
{
s[0]=0; mp.clear();
memset(a,0,sizeof(a));
for(int i=1;i<=m;i++)
{
scanf("%s",ch);
for(int j=0;j<n;j++)
a[i]+=(LL)(ch[n-j-1]-48)*p[j];
s[i]=s[i-1]^a[i]; if (i>=L&&i<=R) mp[s[i]]++; //有区间限制,则只加入一段的异或和
}
LL ans=0;
LL Final=p[n]-1;
for(int i=1;i<=m;i++)
{
ans+=mp[Final]; //每次的贡献是对应状态的数量
Final^=a[i]; //左端点右移就要对状态异或原来的左端点
if (i+R<=m) mp[s[i+R]]++; //对于区间限制,维护map中状态种类及数量
if (i+L-1<=m) mp[s[i+L-1]]--; //无效状态减去,新增的有效状态加入
}
printf("Case %d: %lld\n",++T_T,ans);
}
return 0;
}