zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Java中的四种内部类
    用输入/输出写一个程序,让用户输入一些姓名和电话号码
    分批读取大数据问题
    Linux产生序列数字
    两个有序链表的合并
    int和Integer的区别
    wait()和sleep()的区别
    Unix和Windows文件格式转化
    截取字符串的代码实现
    查看系统信息
  • 原文地址:https://www.cnblogs.com/violetholmes/p/14227276.html
Copyright © 2011-2022 走看看