#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));
}
}
CF1142B AC代码+详细注释
最新推荐文章于 2025-12-16 14:17:05 发布

1122

被折叠的 条评论
为什么被折叠?



