1 条题解
-
0
进行朴素 dp,那么对 建立子序列自动机之后,每次会沿着两个自动机的一条边走,于是设 表示,当前匹配到了 序列的第 位和 序列的第 位,可以 转移。
考虑求出 之前最靠右的与它相同的数的位置,那么若有 ,它能向前贡献的位置是一个矩形,用差分数组动态做前缀和便能做到 。
考察 dp 转移,发现有两种:
- 转移一:先手选择当前匹配位置不同的数。这样一定会走到两个序列的连续段的开头。如果仅能通过这种转移就能走到一个先手必败态,那么这个状态就是必胜态了。
- 转移二:先手选择当前匹配位置相同的数。我们发现,转移形式十分单一。那么我们相当于:每次 同时跳到最靠前的 ,使得 ,,同时检查是否能通过转移一转移到必败态。
发现“是否能通过转移一转移到必败态”只取决于 分别处于哪一连续段中,那么转移二可以 实现。然后对于两个序列的连续段的每种合法搭配,处理“是否能通过转移一转移到必败态”,以及 分别两个序列的连续段的开头时,对应的 。可以在 内实现,使用前面的优化可以变成 。此时查询也可 实现。
瓶颈在于转移二,发现如果我们把所有 相同的位置提出来,以 为横轴, 为纵轴,画一个方阵,那么相当于:加入矩形,每次查询一条从指定位置出发、与 平行的射线第一次接触到矩形是什么时候。 ::::info[解释]{open} 考虑方格被矩形覆盖相当于“能通过转移一转移到必败态”,然后由于我们已经把 相同的位置提出来了,那么每次便是向右上移动一格。 ::::
可以发现:每个矩形是不相交的,且任意两个矩形两维对应的区间,要么相同要么不交。而且我们实际上允许半在线地做这件事,所以我们像朴素 dp 一样,从上到下,从右到左进行扫描。如果我们按照扫描顺序加入矩形,对于一条射线而言,后覆盖的矩形一定比先覆盖的矩形优,所以我们用
std::set维护颜色段可以做到 的复杂度。#include <bits/stdc++.h> #define ll long long using namespace std; inline ll Read() { int sig = 1; ll num = 0; char c = getchar(); while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); } while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar(); return num * sig; } void Write(ll x) { if(x < 0) putchar('-'), x = -x; if(x > 9) Write(x / 10); putchar((x % 10) ^ 48); } const int N = 1605, Q = 1000005, inf = 1e9 + 114514; int k, q, lst[N], g[N][N]; bool f[N][N], ans[Q]; vector<pair<pair<int, int>, int> > query[N][N]; struct Seq { int L, n, sl[N], cl[N], l[N], v[N], lst[N][N]; void Init() { int i, j; for(i = 1; i <= n; i++) sl[i] = l[i] = Read(), sl[i] += sl[i - 1], v[i] = Read(); for(i = 1; i <= k; i++) { lst[i][0] = 0; for(j = 1; j <= n; j++) lst[i][j] = v[j] == i ? j : lst[i][j - 1]; } for(i = 1; i <= n; i++) cl[i] = cl[lst[v[i]][i - 1]] + l[i]; } }a, b; bool Calc(int sx, int sy, int x, int y) { return (sy - sx <= y - x ? y - sy : x - sx) & 1; } struct DS { set<pair<int, pair<int, int> > > st; void Split(int x) { auto p = st.lower_bound(make_pair(x, make_pair(0, 0))); if(p->first != x) st.emplace(x, p->second); } void Cover(int l, int r, int x, int y) { Split(l - 1), Split(r); auto p = *st.lower_bound(make_pair(l, make_pair(0, 0))); while(p.first <= r) st.erase(p), p = *st.lower_bound(make_pair(l, make_pair(0, 0))); st.emplace(r, make_pair(x, y)); } void Insert(int x, int y, int z, int w) { Cover(y - z, w - x, x, y); } bool Query(int sx, int sy) { auto p = *st.lower_bound(make_pair(sy - sx, make_pair(0, 0))); return Calc(sx, sy, p.second.first, p.second.second); } }ds[N]; int main() { int i, j; a.L = Read(), a.n = Read(), b.L = Read(), b.n = Read(); k = Read(), q = Read(), a.Init(), b.Init(); for(i = 1; i <= k; i++) { int x = a.lst[i][a.n], y = b.lst[i][b.n]; if(x && y) { ds[i].st.emplace(b.cl[y] - a.cl[x], make_pair(a.cl[x] + 1, 0)); ds[i].st.emplace(b.cl[y], make_pair(0, b.cl[y] + 1)); } } for(i = 1; i <= q; i++) { int x = Read(), y = Read(); int px = upper_bound(a.sl + 1, a.sl + a.n + 1, x) - a.sl, py = upper_bound(b.sl + 1, b.sl + b.n + 1, y) - b.sl; query[px][py].emplace_back(make_pair(x + 1, y + 1), i); } for(i = a.n; i; i--) for(j = b.n; j; j--) { g[i][j] += g[i + 1][j] + g[i][j + 1] - g[i + 1][j + 1]; if(a.v[i] == b.v[j]) { if(g[i][j] > 0) f[i][j] = true, ds[a.v[i]].Insert(a.cl[i] - a.l[i] + 1, b.cl[j] - b.l[j] + 1, a.cl[i], b.cl[j]); else f[i][j] = ds[a.v[i]].Query(a.cl[i] - a.l[i] + 1, b.cl[j] - b.l[j] + 1) ^ 1; if(!f[i][j]) { int tx = a.lst[a.v[i]][i - 1], ty = b.lst[b.v[j]][j - 1]; g[i - 1][j - 1]++, g[i - 1][ty]--, g[tx][j - 1]--, g[tx][ty]++; } for(auto p : query[i][j]) { if(g[i][j] > 0) ans[p.second] = true; else { int x = p.first.first - a.sl[i - 1] + a.cl[i] - a.l[i]; int y = p.first.second - b.sl[j - 1] + b.cl[j] - b.l[j]; ans[p.second] = ds[a.v[i]].Query(x, y); } } } } for(i = 1; i <= q; i++) printf(ans[i] ? "Yes\n" : "No\n"); }
- 1
信息
- ID
- 10197
- 时间
- 2500ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者