CF1142B Solution

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;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14227276.html