1 条题解

  • 0
    @ 2026-5-6 16:22:27

    进行朴素 dp,那么对 a,ba, b 建立子序列自动机之后,每次会沿着两个自动机的一条边走,于是设 fi,jf_{i, j} 表示,当前匹配到了 aa 序列的第 ii 位和 bb 序列的第 jj 位,可以 O(NMk)O(NMk) 转移。

    考虑求出 ai,bia_i, b_i 之前最靠右的与它相同的数的位置,那么若有 fi,j=0f_{i, j} = 0,它能向前贡献的位置是一个矩形,用差分数组动态做前缀和便能做到 O(NM)O(NM)

    考察 dp 转移,发现有两种:

    • 转移一:先手选择当前匹配位置不同的数。这样一定会走到两个序列的连续段的开头。如果仅能通过这种转移就能走到一个先手必败态,那么这个状态就是必胜态了。
    • 转移二:先手选择当前匹配位置相同的数。我们发现,转移形式十分单一。那么我们相当于:每次 i,ji, j 同时跳到最靠前的 i,ji', j',使得 ai=aia_i = a_{i'}bj=bjb_j = b_{j'},同时检查是否能通过转移一转移到必败态。

    发现“是否能通过转移一转移到必败态”只取决于 i,ji, j 分别处于哪一连续段中,那么转移二可以 O(n+m)O(n + m) 实现。然后对于两个序列的连续段的每种合法搭配,处理“是否能通过转移一转移到必败态”,以及 i,ji, j 分别两个序列的连续段的开头时,对应的 fi,jf_{i, j}。可以在 O(nm(n+m+k))O(nm(n + m + k)) 内实现,使用前面的优化可以变成 O(nm(n+m))O(nm(n + m))。此时查询也可 O(q(n+m))O(q(n + m)) 实现。

    瓶颈在于转移二,发现如果我们把所有 ai,bja_i, b_j 相同的位置提出来,以 ii 为横轴,jj 为纵轴,画一个方阵,那么相当于:加入矩形,每次查询一条从指定位置出发、与 y=xy = x 平行的射线第一次接触到矩形是什么时候。 ::::info[解释]{open} 考虑方格被矩形覆盖相当于“能通过转移一转移到必败态”,然后由于我们已经把 ai,bja_i, b_j 相同的位置提出来了,那么每次便是向右上移动一格。 ::::

    可以发现:每个矩形是不相交的,且任意两个矩形两维对应的区间,要么相同要么不交。而且我们实际上允许半在线地做这件事,所以我们像朴素 dp 一样,从上到下,从右到左进行扫描。如果我们按照扫描顺序加入矩形,对于一条射线而言,后覆盖的矩形一定比先覆盖的矩形优,所以我们用 std::set 维护颜色段可以做到 O(nmlog(n+m)+qlog(n+m))O(nm \log (n + m) + q \log (n + m)) 的复杂度。

    #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

    「OOI 2025 Day 1」爱丽丝、鲍勃和两个数组

    信息

    ID
    10197
    时间
    2500ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者