1608 - Non-boring sequences(折半递归。。暂且这么叫吧)

该题给一个序列,让我们判断是不是不无聊序列(如果不明白请看样例), 我们可以将区间从大到小不断压缩来确定答案,首先要确定一个区间是否满足要求,只要看这个区间里是不是有一个只出现一次的数,受前面《唯一的雪花》一题的启发,我们可以在O(n)时间内求出当前数离他最近的与他相同的数的位置,用数组保存,那么可以在O(1)的时间快速判断,这样就将时间复杂度降到O(n^2) 但是这样还是不够的,会超时。

紫书上给出了一个很巧妙的方法,那就是在递归的时候折半枚举   , 时间复杂度变成了 T(n) = 2*T(n/2) + O(n)  。 让我们变一下形式,T(n)/n = T(n/2)/(n/2) + 2;  令T(n)/n = C(n);

则C(n) - C(n/2) = 2; 令n = 2^k;   则令k = 1,2,3.....  然后累加就得到 C(2^k) = C(1) + 2*k   ,所以C(n) = C(1)+ 2*logn    (以2为底) 所以时间复杂度是n*logn

这个公式叫主定理,大家可以自己查阅。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 200000+5;
int T,n,a[maxn],pre[maxn],nex[maxn];
map<int,int> cur ;
bool is_unique(int p,int l,int r){
    return pre[p]<l&&nex[p]>r;
}
bool check(int l,int r){
    if(l>=r) return true;
    for(int i=l;i-l<=r-i;i++){ //折半递归
        if(is_unique(i,l,r)){
            return check(i+1,r)&&check(l,i-1);
        }
        if(is_unique(r-i+l,l,r))
            return check(r-i+l+1,r)&&check(l,r-i+l-1);
    }
    return false;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        cur.clear();
        for(int i=0;i<n;i++){
            if(!cur.count(a[i])) pre[i] = -1;
            else pre[i] = cur[a[i]];
            cur[a[i]] = i;
        }
        cur.clear();
        for(int i=n-1;i>=0;i--){
            if(!cur.count(a[i])) nex[i] = n;
            else nex[i] = cur[a[i]];
            cur[a[i]] = i;
        }
        if(check(0,n-1)) printf("non-boring\n");
        else printf("boring\n");
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-19 23:25:35

1608 - Non-boring sequences(折半递归。。暂且这么叫吧)的相关文章

UVa 1608,Non-boring sequences

好诡异的一个题啊 紫书上关于从左边找还是从两边往中间找的讨论没有看懂,怎么一下就找到唯一的元素了(⊙_⊙?) 方法就是用的书上讲的方法,类似于uva 11572,不过这个题需要预处理存下两边的最近的相同数的位置 for (int i=1;i<=n;i++) { prev[i]=r[a[i]]; next[prev[i]]=i; r[a[i]]=i;}//记录元素a[i]上次出现的位置,因为是从左向右遍历,所以上次出现的位置正好是prev[i]要求的 //prev[i],与 i位置的元素 相同的左

hdu 5694(BD String,折半递归)

1 #include <bits/stdc++.h> 2 #define lld I64d 3 using namespace std; 4 typedef long long ll; 5 vector<ll> vec; 6 /** 7 *lower_bound 返回大于或等于x 位置的指针 8 * 9 * 思路:先预处理出对称的下标放入vector 10 * dfs(x) 表示 1-x 中 B的个数 11 * (1)若x正好等于vec[i], 则 B 的个数 = vec[i-1]

语言总结&mdash;C/C++

参考<程序员面试宝典> 1. 基本概念 1.1 赋值语句 例1. 按位与操作,例如:a=3,b=3,a&b值等于 0011 & 0011 结果还是0011,那么值还是3: a=4,b=3,a|b:按位或操作, 0100 | 0011 结果是0111,输出的值为7:a||b, a和b进行或运算,只要两者有一个为真则结果即为1. 例2. while(x) { count++; x=x&(x-1);} 此循环用来求输入值x转化为二进制后1的个数.eg:9(1001)&

STL中排序算法

[1]    push_heap:默认为大根堆,主要是上滤操作. [2]    make_heap:创建堆,默认构建大根堆.他的实现基于这样一个简单的想法:将二叉树中的每个仅二层的子树都构成堆,那么整个数据集的布局几乎即可以满足堆的定义. [3]    pop_heap:将已构成堆的迭代器区间中最大值元素移到区间的最后元素位置.原来的最后元素调整为根结点元素后,再对除最后一个元素之外的区间调整. [4]    sort_heap:堆排序,时间复杂度O(nlog2n).内部通过一个while循环调

UVA 1608 Non-boring sequences (分冶+递归)

一.题目概述 二.题目释义 要求序列s的所有子序列,若所有这些子序列中均存在一个自己序列内是唯一的数,则称这个序列s是不无聊的 三.思路分析 从序列s开始考虑,序列s的整个长度为n,若序列s中存在一个唯一数,则所有包含了这个数的子序列都是不无聊的,那么有可能不满足的要求的子序列只会存在于这个唯一数的左区间与有区间,那么问题就被分冶为了看左区间与右区间内是否有一个唯一数,若找到了,则继续这样递归下去,直到靠近区间长度为1为止 那么我们怎么样做到快速的查找在区间[l,r]内存在着唯一数呢,我们容易想

C++算法学习(1)--折半查找(递归和非递归实现)

1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; //折半查找(非递归调用) 6 bool binarySearch(int *arr,int low,int high,int key) 7 { 8 while (low<=high) {//必须为有= 9 int mid = (low + high) / 2; 10 if (arr[mid] > key) { 11 high =

折半查找的递归改写

[问题描述] 针对以非递增有序表表示的静态查找表,编写递归的折半查找算法. [输入形式]该静态查找表从下标1开始存放数据,存放数据按照非递增顺序,具体输入形式如下: 5   //输入元素个数 33 29 25 20 12 //按照非递增顺序连续输入多个数,每个数之间用一个空格隔开 29 //输入需要查找的数[输出形式]如果找到则输出该数据在查找表中的下标,如果找不到则输出0 如 查找29则会输出2[样例输入]5 33 29 25 20 12 29 [样例输出] 2 #include<stdio.

折半算法的C#实现方式-递归和非递归

这个算法,相信大家都懂,但是不真正的手动写一遍,总觉得不得劲.这不,手动写一遍就是有不一样的效果出现了. 往左折半,还是往右走比较简单,其实这两个算法最关键的是:退出条件 min > max  和下次折半时下标或上标位置要+1或-1 /// <summary> /// 递归的纯算法实现 /// </summary> /// <param name="arrList"></param> /// <param name="

●UVA 1608 Non-boring sequences

题链: https://vjudge.net/problem/UVA-1608#author=chenchonghan题解: 分治 如果一个区间[l,r]里面在p位置出现了一个只出现一次的元素,(如果不存在该元素,该区间就是boring的) 那么必然包含p的子区间都是non-boring的. 即如果存在boring的区间,必然是[l,p-1],[p+1,r]的子区间. 所以继续递归处理上面两个区间即可. (判断某个元素是否在区间里只出现一次,只需考虑它左边第一个与它相同的元素或它右边第一个相同的