CF1142B Solution
题解
这道题的数据范围是(2e5),所以算法的时间复杂度需要在(O(nlogn))以内,又因为(qle 2e5),所以要么可以在(O(logn))的复杂度内查询,要么是静态算法。经思考前者不太可行,因此这道题是(O(nlogn))以内的静态算法,可以想到倍增与ST表。(思考过程)
若想使子序列为排列(p)的一个轮换,只要保证子序列中每一个元素的前驱与(p)中该元素的前驱相同即可,而(a)数组中这个前驱的最近位置是固定的。因此设数组(pre[i][j])表示(a)数组中下标为(i)的数前(2^j)个(含(a[i]))以(p)的轮换排列的数的下标,也就是用倍增的方法记录下以(a[i])为右端点的、符合要求子序列的左端点。对于每个询问,我们只需计算(lle ile r)时左端点的最大值,如果这个最大值大于(l),说明存在符合要求且在([l,r])之间的序列。这个区间最大值可以用ST表处理。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,logn=18;
int pre[N][20],a[N],p[N],idp[N],ida[N];
//pre[i][j]:a数组中下标为i的数前2^j个以p排列的数的下标
//idp[i]:p数组中值为i的元素下标
//ida[i]:a数组中坐标小于目前循环且值为i元素的下标
int st[N][20],lg[N];
//st[i][j]:a数组中从下标为i的数后2^j个(含a[i])中选择右端点,子序列的最近左端点的下标
int main()
{
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) {scanf("%d",&p[i]); idp[p[i]]=i;}
p[0]=p[n];
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
//处理pre数组
for(int i=1;i<=m;i++) {pre[i][0]=ida[p[idp[a[i]]-1]]; ida[a[i]]=i;}
for(int j=1;j<=logn;j++)
for(int i=1;i<=m;i++) pre[i][j]=pre[pre[i][j-1]][j-1];
//处理ST表
for(int i=2;i<=m;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=m;i++)
{
st[i][0]=i;
for(int j=logn;j>=0;j--)
if((n-1)&(1<<j)) st[i][0]=pre[st[i][0]][j];
}
for(int j=1;j<=logn;j++)
for(int i=1;i+(1<<j)-1<=m;i++) st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
//回答询问
int l,r;
while(q--)
{
scanf("%d%d",&l,&r);
int qwq=lg[r-l+1];
int qaq=max(st[l][qwq],st[r-(1<<qwq)+1][qwq]);
if(qaq>=l) printf("1");
else printf("0");
}
return 0;
}