CF1142B,代码:

CF1142B,代码:
 

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int LOG = 20;  // 2^20 > 200000,足够覆盖所有情况
int n, m, q;
int p[MAXN], a[MAXN], pos[MAXN];  // pos记录每个数在排列p中的位置
int nxt[MAXN][LOG];  // 倍增数组:nxt[i][j]表示从位置i跳2^j步到达的位置
int last[MAXN];  // last[i]表示从位置i开始匹配一个完整循环移位后到达的位置
int st[MAXN][LOG], lg[MAXN];  // ST表相关数组
// ST表查询区间最小值
int query(int l, int r) {
    int k = lg[r - l + 1];
    return min(st[l][k], st[r - (1 << k) + 1][k]);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    // 读入数据
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
        pos[p[i]] = i;  // 记录每个数在排列p中的位置
    }
    for (int i = 1; i <= m; i++) 
        cin >> a[i];
    // 预处理每个数在a中下一个出现的位置
    // next_pos[x]记录数字x在a中下一个出现的位置,初始设为m+1(表示不存在)
    vector<int> next_pos(n + 1, m + 1);
    // 从后往前处理a数组,这样可以快速找到每个数后面最近的出现位置
    for (int i = m; i >= 1; i--) {
        int x = a[i];  // 当前数字
        // 计算x在排列p中的下一个数字(考虑循环)
        // 例如:如果p=[2,1,3],x=2在p中位置是1,下一个就是p[2]=1
        // 如果x=3在p中位置是3,下一个就是p[1]=2(循环)
        int next_val = p[pos[x] % n + 1];
        // 记录从位置i跳1步(匹配下一个数字)到达的位置
        nxt[i][0] = next_pos[next_val];
        // 更新当前数字x的最新出现位置
        next_pos[x] = i;
    }
    // 倍增预处理:计算跳2^j步到达的位置
    for (int j = 1; j < LOG; j++) 
        for (int i = 1; i <= m; i++) 
            if (nxt[i][j-1] <= m) 
                // 从i跳2^(j-1)步到位置k,再从k跳2^(j-1)步
                nxt[i][j] = nxt[nxt[i][j-1]][j-1];
            else 
                nxt[i][j] = m + 1;  // 超出范围
    // 计算每个位置跳n-1步后到达的位置(匹配一个完整的循环移位)
    for (int i = 1; i <= m; i++) {
        int cur = i;  // 当前位置
        int steps = n - 1;  // 需要跳的步数(匹配n个数字,第一个已经匹配,还需要n-1步)
        
        // 二进制拆分:从高位到低位尝试跳步
        for (int j = LOG - 1; j >= 0; j--) {
            if (steps >= (1 << j)) {
                steps -= (1 << j);
                cur = nxt[cur][j];
                if (cur > m) break;  // 如果已经超出范围,提前结束
            }
        }
        last[i] = cur;  // 记录从位置i开始匹配完整循环移位后到达的位置
    }
    // 预处理ST表:用于快速查询区间最小值
    // 预处理对数数组,用于ST表查询
    lg[1] = 0;
    for (int i = 2; i <= m; i++) 
        lg[i] = lg[i/2] + 1;
    // 初始化ST表第一层
    for (int i = 1; i <= m; i++) 
        st[i][0] = last[i];
    // 构建ST表
    for (int j = 1; j <= lg[m]; j++) 
        for (int i = 1; i + (1 << j) - 1 <= m; i++) {
            st[i][j] = min(st[i][j-1], st[i + (1 << (j-1))][j-1]);
    // 处理查询
    string ans;
    while (q--) {
        int l, r;
        cin >> l >> r;
        // 查询[l, r]区间内last的最小值
        // 如果最小值<=r,说明存在某个位置i∈[l,r],从i开始可以匹配一个完整的循环移位
        // 且匹配结束的位置也在[l,r]范围内
        if (query(l, r) <= r) {
            ans += '1';
        } else {
            ans += '0';
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值