CF1142B AC代码+详细注释

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 2e5 + 2;
// 数组定义:
// p: 输入的排列
// a: 输入的序列
// lst[x]: 数字x在a中最后一次出现的位置
// inxt[x]: 在排列p的环中,x的下一个元素(循环后继)
// pre[i]: 在a数组中,与a[i]相同的前一个位置
// c[i]: 关键数组!记录从位置i开始,按照p顺序匹配时下一个元素的位置
// dep[i]: 在树中的深度
// nd[d]: 深度为d时访问的节点
// r[i]: 核心数组!表示从位置i开始能够形成完整循环移位的最右端点
// lj, nxt, fir: 链式前向星建图用

int p[N], a[N], lst[N], inxt[N], pre[N], c[N], dep[N], nd[N], r[N], lj[N], nxt[N], fir[N];
int n, m, q, i, j, cc, bs;

// 链式前向星添加边
inline void add(int x, int y) {
    lj[++bs] = y;
    nxt[bs] = fir[x];
    fir[x] = bs;
}

// 快速读入整数
inline void read(int &x) {
    cc = getchar();
    while ((cc < 48) || (cc > 57)) cc = getchar();
    x = cc ^ 48;
    cc = getchar();
    while ((cc >= 48) && (cc <= 57)) {
        x = x * 10 + (cc ^ 48);
        cc = getchar();
    }
}
// DFS遍历构建的树
void dfs(int x) {
    int i;
    nd[dep[x]] = x;  // 记录当前深度对应的节点
    // 如果深度足够形成长度为n的序列
    if (dep[x] >= n)
        r[x] = nd[dep[x] - (n - 1)];  // 计算能形成循环移位的最左端点
    else
        r[x] = m + 1;  // 无法形成完整的循环移位

    // 遍历所有子节点
    for (i = fir[x]; i; i = nxt[i]) {
        dep[lj[i]] = dep[x] + 1;  // 子节点深度+1
        dfs(lj[i]);  // 递归遍历
    }
}
int main() {
    read(n); read(m); read(q);
    // 读入排列p,并构建循环后继关系
    for (i = 1; i <= n; i++) {
        read(p[i]);
        // 构建循环:每个元素指向下一个元素,最后一个指向第一个
        if (i > 1) inxt[p[i]] = p[i - 1];
    }
    inxt[p[1]] = p[n];  // 完成循环
    // 读入序列a,并构建关键数组c
    for (i = 1; i <= m; i++) {
        read(a[i]);
        pre[i] = lst[a[i]];  // 记录相同数字的前一个位置
        lst[a[i]] = i;       // 更新该数字最后出现的位置
        // 关键部分:寻找a[i]在排列p中的前驱的匹配位置
        // 对于a[i]的前驱(在p环中),在a中寻找可以匹配的位置
        for (j = lst[inxt[a[i]]]; j && !c[j]; j = pre[j])
            c[j] = i;  // 位置j的下一个匹配位置是i
    }

    // 对于没有找到匹配的位置,设置c[i] = m+1
    for (i = 1; i <= m; i++)
        if (!c[i])
            c[i] = m + 1;

    // 根据c数组构建树:c[i] -> i 的边
    for (i = 1; i <= m; i++)
        add(c[i], i);

    // 从虚拟根节点m+1开始DFS
    dep[m + 1] = 0;
    dfs(m + 1);

    // 预处理r数组:从右往左取最小值
    // r[i]表示从i开始往右,能形成循环移位的最左端点的最小值
    for (i = m - 1; i; i--)
        r[i] = min(r[i + 1], r[i]);

    // 处理每个查询
    while (q--) {
        read(i); read(j);
        // 如果从i开始能在j之前形成循环移位,输出1,否则输出0
        putchar(48 + (r[i] <= j));
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值