• 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");
    	}
    } 
    

      

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

  • 相关阅读:
    约束constraint
    多表查询
    多表关系
    vue 页面跳转的两种方式
    Java三大特性
    如何搭建vue搭建手脚架(vue-cli 3.0以上版本)
    Mysql高版本不兼容group by解决方案
    springboot整合shiro 报 This application has no explicit mapping for /error, so you are seeing this as a fallback. 错误
    使用Springboot整合redis与mysql
    Springboot登录拦截器
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10658118.html
Copyright © 2020-2023  润新知