[NBUT 1646 Internet of Lights and Switches] 前缀异或和+二分
题意描述:N 个灯, M 个开关,每个开关可以控制多个灯。每个开关对应一个01串,第 i 位为0 表示 这个开关不能控制第i 盏灯, 为1 表示能够控制。初始灯是全亮的。你可以按一个连续区间的开关使得灯全灭,区间的长度必须是在[a, b],问 有多少种 方法可以使灯全灭。每个开关只能按一次。
解题思路:显然,按区间[L, R] 的开关等价于 先按一次[1, L - 1], 然后再按一次[L, R] 区间的开关。所以我们首先需要处理出前 i 盏灯的 前缀异或和, 用pre[] 表示。 要让灯全灭, 就相当于找出区间[L, R], 满足 这个区间 的异或和全为1。 也就等价于pre[R] ^ pre[L - 1] 全1。知道这个就简单很多了。我们只需要枚举区间左端点,然后二分区间右端点。然后求出区间长度就OK了。
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
typedef __int64 LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const double eps = 1e-10;
const int MAXN = 50 + 5;
const int MAXM = 300000 + 5;
const int INF = 0x3f3f3f3f;
int T, N, M, A, B;
char buf[MAXN];
LL pre[MAXM], X, Y, E;
PLL data[MAXM];
LL F (char s[]) {
LL ret = 0LL;
for (int i = 0; s[i]; i++) {
ret = (ret << 1) + s[i] - '0';
}
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
// FOUT;
#endif // ONLINE_JUDGE
int cas = 0;
LL res;
while (~scanf ("%d %d %d %d", &N, &M, &A, &B) ) {
res = E = pre[0] = 0LL;
for (int i = 1; i <= N; i++) {
E = (E << 1) | 1;
}
data[0] = PLL (0, 0);
for (int i = 1; i <= M; i++) {
scanf ("%s", buf);
X = F (buf);
pre[i] = pre[i - 1] ^ X;
data[i].fst = pre[i];
data[i].snd = i;
}
sort (data, data + M + 1);
for (int i = 0; i <= M; i++) {
X = data[i].fst;
Y = data[i].snd;
int a = lower_bound (data + 1, data + M + 1, PLL (X ^ E, Y + A) ) - data;
int b = upper_bound (data + 1, data + M + 1, PLL (X ^ E, Y + B) ) - data;
if (a > M) continue;
res += (b - a);
}
printf ("Case %d: %I64d\n", ++cas, res);
}
return 0;
}