Non-boring sequences (启发式分治)

发布时间 2023-04-13 16:27:10作者: VxiaohuanV


题目大意:
对一个序列,如果其任意子区间都有至少一个数只出现一次,那么则称这个序列为non-boring的,否则为boring

思路:

  • 中 数 只出现一次, 极值对关于区间的点对问题,可以通过这个点把区间分成2个部分,分治解决
  • 预处理每个数上一次出现的位置以及下一次出现的位置
  • 对一个区间,如果有一个数满足条件,则以这个数为分界点,对两边进行分治
  • 在枚举满足条件的分界点时从区间两边向中间进行枚举,这样只枚举了n/2
  • 使本身可能成为On的算法优化到Onlogn
  • 大致证明可由 T(n) = 2 * T(n/2) + O(n) 得到
#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
using namespace std;

const int maxn= 200000+5;
int a[maxn];
int l[maxn],r[maxn];

map<int,int> m;
int n;

bool judge(int left,int right)
{
    if(left>=right)
        return true;
    for(int i=0;i<=(right-left)/2;i++)
    {
        if(l[left+i]<left && r[left+i]>right)
            return judge(left,left+i-1)&&judge(left+i+1,right);
        if(l[right-i]<left && r[right-i]>right)
            return judge(left,right-i-1)&&judge(right-i+1,right);
    }
    return false;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",a+i);
        //记录第i个元素左边第一个相同元素的位置
        m.clear();
        for(int i=0;i<n;i++)
        {
            if(m.count(a[i]))
                l[i]=m[a[i]];
            else
                l[i]=-1;
            m[a[i]]=i;
        }
        //记录第i个元素右边第一个相同元素的位置
        m.clear();
        for(int i=n-1;i>=0;i--)
        {
            if(m.count(a[i]))
                r[i]=m[a[i]];
            else
                r[i]=n;

            m[a[i]]=i;
        }
        if(judge(0,n-1))
            cout<<"non-boring"<<endl;
        else
            cout<<"boring"<<endl;
    }
    return 0;
}
偷来的代码