zoukankan      html  css  js  c++  java
  • Codeforces 1142B Lynyrd Skynyrd

    ---恢复内容开始---

    题意:给你一个排列p和数组a,有t组询问,每次询问一个区间的子序列中是否有p的一个循环排列。

    思路:以p = [3, 1, 2]举例, 我们扫描数组b,假设当前数字是1,那么我们找前面离现在最近的3的位置,然后连一条边。为什么只连最近的呢?比如[3,3,1,2]这种情况,1只需要和第二个3连就行了,因为连第一个3的这种情况已经被连第二个3的这种情况包含了。那么对于每个点,我们可以找到通过连的边走n - 1次所到的点,所有包含这个区间的询问区间都有一个合法的循环排列。但是直接暴力找n - 1次到的点会被卡成O(n^2)的,我们可以用倍增优化这个过程。之后,对于每个点,我们只需记录在它前面的点中满足走n  - 1次的点中最大的那一个就行了,因为这样相当于对于每个点,尽力压缩合法的区间。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 200010;
    int f[maxn][20], pre[maxn], a[maxn], b[maxn], last[maxn], p[maxn], res[maxn];
    int n, m, T, t;
    int solve(int x) {
    	int now = n - 1, ans = x;
    	for (int i = t; i >= 0; i--) {
    		if(p[i] <= now) {
    			ans = f[ans][i];
    			now -= p[i];
    		}
    	}
    	return ans;
    }
    int main() {
    	int l, r;
    	scanf("%d%d%d", &n, &m, &T);
    	t = (int)(log(n) / log(2)) + 1;
    	p[0] = 1;
    	for (int i = 1; i <= t;i++) {
    		p[i] = p[i - 1] * 2; 
    	}
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", &a[i]);
    	}
    	for (int i = 2; i <= n; i++) {
    		pre[a[i]] = a[i - 1];
    	}
    	pre[a[1]] = a[n];
    	for (int i = 1; i <= m; i++) {
    		scanf("%d", &b[i]);
    		f[i][0] = last[pre[b[i]]];
    		for (int j = 1; j <= t; j++) {
    			f[i][j] = f[f[i][j - 1]][j - 1];
    		}
    		last[b[i]] = i;
    	}
    	int pos;
    	for (int i = 1; i <= m; i++) {
    		pos = solve(i);
    		res[i] = max(res[i - 1], pos);
    	}
    	while(T--) {
    		scanf("%d%d", &l, &r);
    		if(res[r] >= l) printf("1");
    		else printf("0");
    	}
    } 
    

      

    ---恢复内容结束---

  • 相关阅读:
    又发现新的QQ系统消息相关dll文件
    修改QQ版本号,解决QQ版本过低
    学DIV+CSS技术,如何入门?
    删除MSN广告及屏蔽MSN网页广告的方法
    制作网页需要掌握的几个基本技能
    陈晓薇的灿烂人生:比美丽更动人
    屏蔽QQ系统广播消息方法
    HTML结构化CSS网页布局入门指南
    Photoshop常用快捷键(逐步熟悉)
    如何去除视频上的水印?
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10658118.html
Copyright © 2011-2022 走看看