比赛链接
官方题解
Problem A. Mind Control
不难发现玩家可以选择到的两个权值仅与之前玩家选择开头元素的次数 i i i 有关。
因此,枚举强制之前玩家选择开头元素的次数,最坏的情况就是对应区间里最小的权值。
时间复杂度 O ( N 2 ) O(N^2) O(N2) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int a[MAXN], b[MAXN];
int main() {
int T; read(T);
while (T--) {
int n, m, k;
read(n), read(m), read(k);
chkmin(k, m - 1);
for (int i = 1; i <= n; i++)
read(a[i]);
for (int i = 0, j = n - m + 1; i <= m - 1; i++, j++)
b[i] = max(a[i + 1], a[j]);
int ans = 0;
for (int i = 0, j = m - k - 1; j <= m - 1; i++, j++) {
int Min = 2e9;
for (int l = i; l <= j; l++)
chkmin(Min, b[l]);
chkmax(ans, Min);
}
cout << ans << endl;
}
return 0;
}
Problem B. Irreducible Anagrams
当 l = r l=r l=r 时答案显然为 Yes 。
若 s l ≠ s r s_l\ne s_r sl=sr ,可以考虑将所有 s l s_l sl 排列在最右侧, s r s_r sr 排列在最左侧,其余字符任意排列,可以证明得到的字符串一定符合条件,因此答案为 Yes 。
否则,即 s l = s r s_l=s_r sl=sr ,若区间内字符种类数在 2 2 2 以内,答案为 No ,种类数为 1 1 1 时显然,种类数为 2 2 2 时可以通过将字符看做 + 1 , − 1 +1,-1 +1,−1 ,作折线图证明。
否则,将 s l s_l sl 排列在中间,某两种字符排在两边,可以证明,在交换字符种类的排列顺序后,同样可以得到一个合法的构造,因此答案为 Yes 。
时间复杂度 O ( ∣ S ∣ + Q ) O(|S|+Q) O(∣S∣+Q) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
char s[MAXN];
int sum[MAXN][26];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 25; j++)
sum[i][j] = sum[i - 1][j] + (s[i] - 'a' == j);
int q; read(q);
while (q--) {
int l, r; read(l), read(r);
if (l == r || s[l] != s[r]) puts("Yes");
else {
int cnt = 0;
for (int i = 0; i <= 25; i++)
cnt += (sum[r][i] - sum[l - 1][i]) != 0;
if (cnt >= 3) puts("Yes");
else puts("No");
}
}
return 0;
}
Problem C. Prefix Enlightenment
将各个集合选或不选看做事件,则各个元素的限制应当形如:
(
1
)
(1)
(1) 、某个集合必须选或不选
(
2
)
(2)
(2) 、某两个集合的选取状态必须相同或不同
将各个事件是否成立拆点,并查集即可。
时间复杂度 O ( N + K ) O(N+K) O(N+K) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 6e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int tr, fl, ans, f[MAXN], s[MAXN][2];
int find(int x) {
if (f[x] == x) return x;
else return f[x] = find(f[x]);
}
char st[MAXN];
vector <int> a[MAXN];
int calc(int x) {
x = find(x);
if (x == tr) return s[x][1];
if (x == fl) return s[x][0];
return min(s[x][1], s[x][0]);
}
void merge(int x, int y) {
if (find(x) == find(y)) return;
x = find(x), y = find(y);
if (x > y) swap(x, y);
f[x] = y, s[y][0] += s[x][0], s[y][1] += s[x][1];
}
int main() {
int n, m; read(n), read(m);
scanf("\n%s", st + 1);
for (int i = 1; i <= m; i++) {
int x; read(x);
while (x--) {
int y; read(y);
a[y].push_back(i);
}
}
for (int i = 1; i <= m; i++) {
f[i] = i, f[i + m] = i + m;
s[i][0] = 1, s[i + m][1] = 1;
}
tr = m * 2 + 1, fl = m * 2 + 2, ans = 0;
f[tr] = tr, f[fl] = fl;
for (int i = 1; i <= n; i++) {
int x, y;
if (a[i].size() != 0) {
if (st[i] == '0') {
if (a[i].size() == 1) {
int x = a[i][0];
if (find(x + m) != find(tr)) {
ans -= calc(x + m);
ans -= calc(tr);
merge(x + m, tr);
merge(x, fl);
ans += calc(x + m);
}
} else {
int x = a[i][0], y = a[i][1];
if (find(x) != find(y + m)) {
ans -= calc(x);
ans -= calc(y);
merge(x + m, y);
merge(x, y + m);
ans += calc(x);
}
}
} else {
if (a[i].size() == 1) {
int x = a[i][0];
if (find(x) != find(tr)) {
ans -= calc(x);
ans -= calc(tr);
merge(x, tr);
merge(x + m, fl);
ans += calc(x);
}
} else {
int x = a[i][0], y = a[i][1];
if (find(x) != find(y)) {
ans -= calc(x);
ans -= calc(y);
merge(x, y);
merge(x + m, y + m);
ans += calc(x);
}
}
}
}
printf("%d\n", ans);
}
return 0;
}
Problem D. Coffee Varieties (hard version)
单独考虑 K = 1 K=1 K=1 的情况。
对于一般的情况,我们希望求出每个元素之前是否出现过。
将相邻的 K 2 \frac{K}{2} 2K 个元素分为一组,按照顺序操作两组元素可以检测后一组的每个元素是否在前一组中出现,因此,我们希望将组与组之间串成若干条链,使得每两组均在某条链上相邻。
则贪心串链即可,贪心的方式可见代码。
时间复杂度 O ( N 2 K ) O(\frac{N^2}{K}) O(KN2) ,操作次数在 3 N 2 2 K \frac{3N^2}{2K} 2K3N2 以内。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
bool ans[MAXN];
vector <int> a[MAXN], b[MAXN], c[MAXN];
int main() {
int n, k; read(n), read(k);
if (k == 1) {
int ans = 0;
for (int i = 1; i <= n; i++) {
bool res = true;
for (int j = 1; j <= i - 1; j++) {
char c;
cout << '?' << ' ' << i << endl;
scanf("\n%c", &c);
cout << '?' << ' ' << j << endl;
scanf("\n%c", &c);
cout << 'R' << endl;
if (c == 'Y') res = false;
}
ans += res;
}
cout << '!' << ' ' << ans << endl;
} else {
for (int i = 1; i <= n; i++)
ans[i] = true;
int m = n / (k / 2);
for (int i = 1, j = 1; i <= m; i++, j += k / 2)
for (int l = 1; l <= k / 2; l++)
a[i].push_back(j + l - 1);
int cnt = 0;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= i - 1; j++) {
if (b[j].size()) {
int tmp = b[j].back();
b[j].pop_back();
c[tmp].push_back(i);
b[i].push_back(tmp);
} else {
cnt++;
c[cnt].push_back(j);
c[cnt].push_back(i);
b[i].push_back(cnt);
}
}
for (int i = 1; i <= cnt; i++) {
for (auto y : c[i])
for (auto x : a[y]) {
char t;
cout << '?' << ' ' << x << endl;
scanf("\n%c", &t);
if (t == 'Y') ans[x] = false;
}
cout << 'R' << endl;
}
int final = 0;
for (int i = 1; i <= n; i++)
final += ans[i];
cout << '!' << ' ' << final << endl;
}
return 0;
}
Problem E. Cartesian Tree
考虑维护各个元素为根的区间 ( l , r ] (l,r] (l,r] 的元素出现次数的前缀和 L i , R i L_i,R_i Li,Ri 。
若可以很好地维护它们,答案显然应为 ∑ R i − ∑ L i \sum R_i-\sum L_i ∑Ri−∑Li 。
则加入一个在
x
x
x 处的元素
v
v
v 对上述数组的影响是:
(
1
)
(1)
(1) 、对
[
x
,
N
]
[x,N]
[x,N] 内的
L
i
,
R
i
L_i,R_i
Li,Ri 进行
+
1
+1
+1
(
2
)
(2)
(2) 、对
(
x
,
N
]
(x,N]
(x,N] 内的
L
i
L_i
Li 进行
L
i
=
m
a
x
{
L
i
,
v
}
L_i=max\{L_i,v\}
Li=max{Li,v}
(
3
)
(3)
(3) 、对
[
1
,
x
)
[1,x)
[1,x) 内的
R
i
R_i
Ri 进行
R
i
=
m
i
n
{
R
i
,
v
−
1
}
R_i=min\{R_i,v-1\}
Ri=min{Ri,v−1}
(
4
)
(4)
(4) 、将
(
L
x
,
R
x
)
(L_x,R_x)
(Lx,Rx) 修改为
(
0
,
v
)
(0,v)
(0,v)
用 SegmentTree Beats 分别维护 L i , R i L_i,R_i Li,Ri 即可。
注意到全程中,空点始终满足 L i = R i L_i=R_i Li=Ri ,不会影响答案。
时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
struct SegmentTreeChkmax {
struct Node {
ll sum; int lc, rc, tag, len;
int cntMin, Min, Nin, tagMin;
} a[MAXN * 2];
int n, root, size;
void update(int root) {
a[root].cntMin = 0;
a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min);
a[root].Nin = min(a[a[root].lc].Nin, a[a[root].rc].Nin);
if (a[a[root].lc].Min != a[root].Min) chkmin(a[root].Nin, a[a[root].lc].Min);
else a[root].cntMin += a[a[root].lc].cntMin;
if (a[a[root].rc].Min != a[root].Min) chkmin(a[root].Nin, a[a[root].rc].Min);
else a[root].cntMin += a[a[root].rc].cntMin;
}
void build(int &root, int l, int r) {
root = ++size;
a[root].len = r - l + 1;
if (l == r) {
a[root].sum = 0;
a[root].Min = 0;
a[root].cntMin = 1;
a[root].Nin = INF;
return;
}
int mid = (l + r) / 2;
build(a[root].lc, l, mid);
build(a[root].rc, mid + 1, r);
update(root);
}
void init(int x) {
n = x;
build(root, 1, n);
}
void pushdown(int root) {
int lc = a[root].lc;
int rc = a[root].rc;
if (a[root].tag) {
a[lc].tag += a[root].tag;
a[lc].sum += 1ll * a[lc].len * a[root].tag;
a[lc].Min += a[root].tag;
a[lc].Nin += a[root].tag;
a[rc].tag += a[root].tag;
a[rc].sum += 1ll * a[rc].len * a[root].tag;
a[rc].Min += a[root].tag;
a[rc].Nin += a[root].tag;
a[root].tag = 0;
}
if (a[root].tagMin) {
int Min = min(a[lc].Min, a[rc].Min);
if (a[lc].Min == Min) {
a[lc].Min += a[root].tagMin;
a[lc].tagMin += a[root].tagMin;
a[lc].sum += 1ll * a[lc].cntMin * a[root].tagMin;
}
if (a[rc].Min == Min) {
a[rc].Min += a[root].tagMin;
a[rc].tagMin += a[root].tagMin;
a[rc].sum += 1ll * a[rc].cntMin * a[root].tagMin;
}
a[root].tagMin = 0;
}
}
void add(int root, int l, int r, int ql, int qr, int d) {
if (l == ql && r == qr) {
a[root].tag += d;
a[root].Min += d;
a[root].Nin += d;
a[root].sum += 1ll * d * a[root].len;
return;
}
pushdown(root);
int mid = (l + r) / 2;
if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), d);
if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
update(root);
}
void add(int l, int r, int d) {
add(root, 1, n, l, r, d);
}
void chkmax(int root, int l, int r, int ql, int qr, int d) {
if (d <= a[root].Min) return;
if (l == ql && r == qr) {
if (d < a[root].Nin) {
a[root].sum += 1ll * a[root].cntMin * (d - a[root].Min);
a[root].tagMin += d - a[root].Min;
a[root].Min = d;
return;
}
pushdown(root);
int mid = (l + r) / 2;
chkmax(a[root].lc, l, mid, l, mid, d);
chkmax(a[root].rc, mid + 1, r, mid + 1, r, d);
update(root);
return;
}
pushdown(root);
int mid = (l + r) / 2;
if (mid >= ql) chkmax(a[root].lc, l, mid, ql, min(mid, qr), d);
if (mid + 1 <= qr) chkmax(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
update(root);
}
void chkmax(int l, int r, int d) {
chkmax(root, 1, n, l, r, d);
}
ll query(int root, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return a[root].sum;
int mid = (l + r) / 2; ll ans = 0;
pushdown(root);
if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(qr, mid));
if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
return ans;
}
ll query(int l, int r) {
return query(root, 1, n, l, r);
}
} L;
struct SegmentTreeChkmin {
struct Node {
ll sum; int lc, rc, tag, len;
int cntMax, Max, Nax, tagMax;
} a[MAXN * 2];
int n, root, size;
void update(int root) {
a[root].cntMax = 0;
a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max);
a[root].Nax = max(a[a[root].lc].Nax, a[a[root].rc].Nax);
if (a[a[root].lc].Max != a[root].Max) chkmax(a[root].Nax, a[a[root].lc].Max);
else a[root].cntMax += a[a[root].lc].cntMax;
if (a[a[root].rc].Max != a[root].Max) chkmax(a[root].Nax, a[a[root].rc].Max);
else a[root].cntMax += a[a[root].rc].cntMax;
}
void build(int &root, int l, int r) {
root = ++size;
a[root].len = r - l + 1;
if (l == r) {
a[root].sum = 0;
a[root].Max = 0;
a[root].cntMax = 1;
a[root].Nax = -INF;
return;
}
int mid = (l + r) / 2;
build(a[root].lc, l, mid);
build(a[root].rc, mid + 1, r);
update(root);
}
void init(int x) {
n = x;
build(root, 1, n);
}
void pushdown(int root) {
int lc = a[root].lc;
int rc = a[root].rc;
if (a[root].tag) {
a[lc].tag += a[root].tag;
a[lc].sum += 1ll * a[lc].len * a[root].tag;
a[lc].Max += a[root].tag;
a[lc].Nax += a[root].tag;
a[rc].tag += a[root].tag;
a[rc].sum += 1ll * a[rc].len * a[root].tag;
a[rc].Max += a[root].tag;
a[rc].Nax += a[root].tag;
a[root].tag = 0;
}
if (a[root].tagMax) {
int Max = max(a[lc].Max, a[rc].Max);
if (a[lc].Max == Max) {
a[lc].Max += a[root].tagMax;
a[lc].tagMax += a[root].tagMax;
a[lc].sum += 1ll * a[lc].cntMax * a[root].tagMax;
}
if (a[rc].Max == Max) {
a[rc].Max += a[root].tagMax;
a[rc].tagMax += a[root].tagMax;
a[rc].sum += 1ll * a[rc].cntMax * a[root].tagMax;
}
a[root].tagMax = 0;
}
}
void add(int root, int l, int r, int ql, int qr, int d) {
if (l == ql && r == qr) {
a[root].tag += d;
a[root].Max += d;
a[root].Nax += d;
a[root].sum += 1ll * d * a[root].len;
return;
}
pushdown(root);
int mid = (l + r) / 2;
if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), d);
if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
update(root);
}
void add(int l, int r, int d) {
add(root, 1, n, l, r, d);
}
void chkmin(int root, int l, int r, int ql, int qr, int d) {
if (d >= a[root].Max) return;
if (l == ql && r == qr) {
if (d > a[root].Nax) {
a[root].sum += 1ll * a[root].cntMax * (d - a[root].Max);
a[root].tagMax += d - a[root].Max;
a[root].Max = d;
return;
}
pushdown(root);
int mid = (l + r) / 2;
chkmin(a[root].lc, l, mid, l, mid, d);
chkmin(a[root].rc, mid + 1, r, mid + 1, r, d);
update(root);
return;
}
pushdown(root);
int mid = (l + r) / 2;
if (mid >= ql) chkmin(a[root].lc, l, mid, ql, min(mid, qr), d);
if (mid + 1 <= qr) chkmin(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
update(root);
}
void chkmin(int l, int r, int d) {
chkmin(root, 1, n, l, r, d);
}
ll query(int root, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return a[root].sum;
int mid = (l + r) / 2; ll ans = 0;
pushdown(root);
if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(qr, mid));
if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
return ans;
}
ll query(int l, int r) {
return query(root, 1, n, l, r);
}
} R;
int n, a[MAXN], home[MAXN];
int main() {
read(n), L.init(n), R.init(n);
for (int i = 1; i <= n; i++)
read(a[i]), home[a[i]] = i;
for (int i = 1; i <= n; i++) {
int x = home[i];
L.add(x, n, 1);
R.add(x, n, 1);
int cnt = L.query(x, x);
if (x + 1 <= n) L.chkmax(x + 1, n, cnt);
if (x - 1 >= 1) R.chkmin(1, x - 1, cnt - 1);
L.add(x, x, 0 - cnt);
R.add(x, x, i - cnt);
printf("%lld\n", R.query(1, n) - L.query(1, n));
}
return 0;
}
Problem F. Making Shapes
问题等价于给各个向量赋上非负的系数,使得向量和为零向量,且各个坐标系的跨度 S x , S y S_x,S_y Sx,Sy 均不超过 M M M ,跨度定义为正数项的和。求满足条件的方案数。
令横坐标正数项的和为
p
x
px
px ,负数项和的绝对值为
n
x
nx
nx ;
令纵坐标正数项的和为
p
y
py
py ,负数项和的绝对值为
n
y
ny
ny 。
则应当有 p x = n x = S x , p y = n y = S y px=nx=S_x,py=ny=S_y px=nx=Sx,py=ny=Sy 。
那么,按照传统的数位 DP 技巧,二进制拆分后,从高位向低位 DP ,
在状态中记录
p
x
,
n
x
,
p
y
,
n
y
px,nx,py,ny
px,nx,py,ny 表示直到当前二进制位
S
x
−
p
x
,
S
x
−
n
x
,
S
y
−
p
y
,
S
y
−
n
y
S_x-px,S_x-nx,S_y-py,S_y-ny
Sx−px,Sx−nx,Sy−py,Sy−ny 的余数,暴力转移即可。
时间复杂度 O ( 2 N × N 4 V 4 L o g M ) O(2^N\times N^4V^4LogM) O(2N×N4V4LogM) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 45;
const int P = 998244353;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int n, m, Log, a[MAXN], x[MAXN], y[MAXN], bit[MAXN];
int dp[2][MAXN][MAXN][MAXN][MAXN][2][2];
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int main() {
read(n), read(m);
while (m != 0) {
a[++Log] = m % 2;
m >>= 1;
}
for (int i = 1; i <= n; i++) {
read(x[i]), read(y[i]);
bit[i] = 1 << (i - 1);
}
int lim = 32;
dp[Log & 1][0][0][0][0][1][1] = 1;
for (int i = Log, s = Log & 1, t = s ^ 1; i >= 1; i--, swap(s, t)) {
memset(dp[t], 0, sizeof(dp[t]));
for (int px = 0; px <= lim; px++)
for (int py = 0; py <= lim; py++)
for (int nx = 0; nx <= lim; nx++)
for (int ny = 0; ny <= lim; ny++) {
int vzz = dp[s][px][py][nx][ny][0][0];
int vzo = dp[s][px][py][nx][ny][0][1];
int voz = dp[s][px][py][nx][ny][1][0];
int voo = dp[s][px][py][nx][ny][1][1];
if (!(vzz || vzo || voz || voo)) continue;
for (int s = 0; s <= (1 << n) - 1; s++) {
int incpx = 0, incpy = 0, incnx = 0, incny = 0;
for (int j = 1; j <= n; j++)
if (s & bit[j]) {
if (x[j] >= 0) incpx += x[j];
else incnx -= x[j];
if (y[j] >= 0) incpy += y[j];
else incny -= y[j];
}
if (px >= incpx && py >= incpy && nx >= incnx && ny >= incny) {
int tpx = (px - incpx) * 2;
int tpy = (py - incpy) * 2;
int tnx = (nx - incnx) * 2;
int tny = (ny - incny) * 2;
if (tpx <= lim && tpy <= lim && tnx <= lim && tny <= lim) {
update(dp[t][tpx][tpy][tnx][tny][0][0], vzz);
update(dp[t][tpx][tpy][tnx][tny][0][a[i] == 0], vzo);
update(dp[t][tpx][tpy][tnx][tny][a[i] == 0][0], voz);
update(dp[t][tpx][tpy][tnx][tny][a[i] == 0][a[i] == 0], voo);
}
}
if (px + 1 >= incpx && py >= incpy && nx + 1 >= incnx && ny >= incny) {
int tpx = (px + 1 - incpx) * 2;
int tpy = (py - incpy) * 2;
int tnx = (nx + 1 - incnx) * 2;
int tny = (ny - incny) * 2;
if (tpx <= lim && tpy <= lim && tnx <= lim && tny <= lim) {
update(dp[t][tpx][tpy][tnx][tny][0][0], vzz);
update(dp[t][tpx][tpy][tnx][tny][0][a[i] == 0], vzo);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][1][0], voz);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][1][a[i] == 0], voo);
}
}
if (px >= incpx && py + 1 >= incpy && nx >= incnx && ny + 1 >= incny) {
int tpx = (px - incpx) * 2;
int tpy = (py + 1 - incpy) * 2;
int tnx = (nx - incnx) * 2;
int tny = (ny + 1 - incny) * 2;
if (tpx <= lim && tpy <= lim && tnx <= lim && tny <= lim) {
update(dp[t][tpx][tpy][tnx][tny][0][0], vzz);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][0][1], vzo);
update(dp[t][tpx][tpy][tnx][tny][a[i] == 0][0], voz);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][a[i] == 0][1], voo);
}
}
if (px + 1 >= incpx && py + 1 >= incpy && nx + 1 >= incnx && ny + 1 >= incny) {
int tpx = (px + 1 - incpx) * 2;
int tpy = (py + 1 - incpy) * 2;
int tnx = (nx + 1 - incnx) * 2;
int tny = (ny + 1 - incny) * 2;
if (tpx <= lim && tpy <= lim && tnx <= lim && tny <= lim) {
update(dp[t][tpx][tpy][tnx][tny][0][0], vzz);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][0][1], vzo);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][1][0], voz);
if (a[i] == 1) update(dp[t][tpx][tpy][tnx][tny][1][1], voo);
}
}
}
}
}
int ans = P - 1;
for (int l1 = 0; l1 <= 1; l1++)
for (int l2 = 0; l2 <= 1; l2++)
update(ans, dp[0][0][0][0][0][l1][l2]);
cout << ans << endl;
return 0;
}