有的时候,碰到一道题,要给自己先设立部分分,再去想如何把部分分推广到一般情况。这题就是绝佳的例子。
不妨将(a_i)用(a_i - 1)替代,这样就变成了(a_i in { 0, 1, 2})了。
我们给自己设立的部分分是(a_i in { 0, 1 })时怎么做。
我们会发现(x_{i, j} equiv x_{i - 1, j} + x_{i - 1, j + 1} (mod 2))了。于是我们在(mod 2)意义下计算出(x_{n, 1})即可。
用简单的归纳法即可得到(x_{n, 1} equiv sum_{i = 1}^{n} {{{n - 1} choose {i - 1}} a_i} (mod 2))。
我们接下来的工作是研究这个做法如何推广。我们发现这个做法能够计算出(x_{n, 1} mod 2)的值。如果发现它模2余1,就可以唯一确定它是1。否则我们要辨别它到底是(0)还是(2)。
如果(x_{1, 1}, ... x_{n, 1})中有一个(1)的话,分析(x_{i, 1}, ..., x_{i, n + 1 - i})这些数。如果它有一些(1)且不全是(1)的话,那么(x_{i + 1, 1}, ..., x_{i + 1, n - i})这一行也必定有(1)。如果每一行都满足这一行的数必定有(1)的话,那么(x_{n, 1} = 1),与我们之前的假设矛盾。因此我们一定有一行全是(1),这样才能生成一个没有(1)的一行。在这一行之后所有数都变成(0)了,所以(x_{n, 1} = 0)。
否则我们又可以假设(x_{1, 1}, ..., x_{n, 1})全部不为(1),将它们通通除以(2)后再使用部分分的算法即可!、
时间复杂度为(O(n)),可以轻松通过此题。
代码如下:
#include <bits/stdc++.h>
#define debug(x) cerr << #x << " " << (x) << endl
using namespace std;
const int N = 1000005;
template <class T>
void read (T &x) {
int sgn = 1;
char ch;
x = 0;
for (ch = getchar(); (ch < '0' || ch > '9') && ch != '-'; ch = getchar()) ;
if (ch == '-') ch = getchar(), sgn = -1;
for (; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
template <class T>
void write (T x) {
if (x < 0) putchar('-'), write(-x);
else if (x < 10) putchar(x + '0');
else write(x / 10), putchar(x % 10 + '0');
}
char str[N];
int n, a[N];
int main () {
read(n), n--;
scanf("%s", str);
for (int i = 0; i <= n; i++) a[i] = str[i] - '1';
int ans = 0;
for (int i = 0; i <= n; i++) {
if ((n & i) == i) ans = (ans + a[i]) % 2;
}
if (!ans) {
bool flag = true;
for (int i = 0; i <= n; i++) {
if (a[i] == 1) flag = false;
}
if (flag) {
int x = 0;
for (int i = 0; i <= n; i++) a[i] >>= 1;
for (int i = 0; i <= n; i++) {
if ((n & i) == i) x = (x + a[i]) % 2;
}
if (x) ans = 2;
}
}
write(ans), putchar('
');
return 0;
}