【题解】「JOISC 2017 Day 3」幽深府邸

dfs + dp 好题

本题考察了对记忆化搜索的应用。

首先不难发现,以位置 i i i 作为起点可以扩展出区间 [ L , R ] [L,R] [L,R] ,且满足 L ≤ i ≤ R L\leq i\leq R LiR

那么我们的任务就是对于每一个 i i i ,找到它的极大区间。

假设当前扩展到了 [ L , R ] [L,R] [L,R] ,考虑 L − 1 L-1 L1 R + 1 R+1 R+1 能否扩展得到,相当于对 i i i L − 1 L-1 L1 连一条边,然后对 L − 1 L-1 L1 进行子问题求解,更新 L = m i n ( L , L x ) L=min(L,L_x) L=min(L,Lx) R = m a x ( R , R x ) R=max(R,R_x) R=max(R,Rx) 。注意将正在遍历的点设为 1 1 1 ,如果 v i s [ x ] = 1 vis[x]=1 vis[x]=1 则跳出。

那么就有一个很优美的性质,对于每一个点 i i i ,我们只进行一次扩展,而且会得到极大区间,同时我们借助了已经跑出的 d p dp dp 值,加快了这一过程。

时间复杂度最坏 O ( n 2 ) O(n^2) O(n2) ,但是如果均摊的话,大概是 O ( n l o g n ) O(nlogn) O(nlogn) 的级别。

#include <bits/stdc++.h>
using namespace std;
const int mx = 5e5 + 5;
inline int read() {
    int X = 0;
    bool flag = 1;
    char ch = getchar();

    while (ch < '0' || ch > '9') {
        if (ch == '-')
            flag = 0;

        ch = getchar();
    }

    while (ch >= '0' && ch <= '9') {
        X = (X << 1) + (X << 3) + ch - '0';
        ch = getchar();
    }

    if (flag)
        return X;

    return ~(X - 1);
}
int n, q, l[mx], r[mx], id[mx], ky[mx], L[mx], R[mx], ps[mx], c[mx], ins[mx];
void dfs(int x) {
    if (ins[x])
        return;

    ins[x] = 1;
    L[x] = R[x] = x;
    int ok = 1;

    while (ok) {
        ok = 0;
        int tl = L[x] - 1, tr = R[x] + 1;

        if (r[tl] && r[tl] < tr) {
            dfs(tl);

            if (L[tl] < L[x])
                L[x] = L[tl], ok = 1;

            if (R[tl] > R[x])
                R[x] = R[tl], ok = 1;
        }

        if (l[tr] && l[tr] > tl) {
            dfs(tr);

            if (L[tr] < L[x])
                L[x] = L[tr], ok = 1;

            if (R[tr] > R[x])
                R[x] = R[tr], ok = 1;
        }
    }
}
int main() {
    //  freopen("data.in","r",stdin);
    n = read();

    for (int i = 1; i < n; i++)
        c[i] = read();

    for (int i = 1; i <= n; i++) {
        id[i] = id[i - 1] + read();

        for (int j = id[i - 1] + 1; j <= id[i]; j++) {
            ky[j] = read();
        }
    }

    for (int i = n; i > 1; i--) {
        for (int j = id[i - 1] + 1; j <= id[i]; j++) {
            ps[ky[j]] = i;
        }

        r[i - 1] = ps[c[i - 1]];
    }

    for (int i = 1; i <= n; i++)
        ps[i] = 0;

    for (int i = 1; i < n; i++) {
        for (int j = id[i - 1] + 1; j <= id[i]; j++) {
            ps[ky[j]] = i;
        }

        l[i + 1] = ps[c[i]];
    }

    for (int i = 1; i <= n; i++)
        dfs(i);

    q = read();

    for (int i = 1; i <= q; i++) {
        int x = read(), y = read();

        if (L[x] <= y && y <= R[x]) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值