Codeforces 1142B Lynyrd Skynyrd
题目链接
https://codeforces.com/problemset/problem/1142/B
题目大意
给你一个排列 $P$ 和一个数组 $A$ ,$Q$ 次询问,每次询问 $A$ 在 $[l, r]$ 之间是否存在一个子串,在循环平移之后和 $P$ 相同。
题目分析
定义 $f[i]$ 为数字 $i$ 的最大的出现位置从 $1$ 到 $n$ 进行 $DP$ ,记录下每一个元素在 $P$ 中的前驱的最大出现位置。统计完成之后再使用倍增,处理所有的元素往前找能找到的元素。随后,对于每一个位置,统计出来由它开始往前能构造出来的 $P$ 的最大起始位置。随后 $O(1)$ 查询即可。
代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <vector> using namespace std; const int maxn = 2e5 + 5; int n, m, q, t; int a[maxn]; int b[maxn]; int fa[maxn][20]; int f[maxn]; int ans[maxn]; int last[maxn]; int read(){ int x; scanf("%d", &x); return x; } int main(){ int i, j; int l, r; n = read(); m = read(); q = read(); for(i=1;i<=n;i++){ a[i] = read(); last[a[i]] = a[i - 1]; } last[a[1]] = a[n]; for(i=1;i<=m;i++){ b[i] = read(); } for(i=1;i<=m;i++){ fa[i][0] = f[last[b[i]]]; f[b[i]] = i; } for(i=1;i<=m;i++){ for(j=0;fa[fa[i][j]][j];j++){ fa[i][j + 1] = fa[fa[i][j]][j]; } } for(i=1;i<=m;i++){ ans[i] = i; int x = n - 1; for(j=20;j>=0;j--){ if(x >= (1 << j)){ ans[i] = fa[ans[i]][j]; x -= (1 << j); } } ans[i] = max(ans[i], ans[i - 1]); } while(q--){ l = read(); r = read(); if(ans[r] >= l){ printf("1"); }else{ printf("0"); } } printf("\n"); return 0; }