題解 UVA1608 【不無聊的序列 Non-boring sequences】

思路:

演算法很顯然:

一、在區間\([l,r]\)找到一個只出現一次的元素P(如果不存在,那麼序列\(boring\)

二、遞歸處理區間\([l,p-1]\)和區間\([p+1,r]\)

其關鍵在於如何找到一個只出現一次的元素P。

首先,我們得知道如何判斷一個元素是不是只出現一次。

我們可以用\(STL\)中的\(map\)記錄與當前元素值相同的上一個元素 or 下一個元素的位置,然後更新即可。

因為\(map\)的所有操作都是\(O(log_n)\)的,所以預處理的時間複雜度為\(O(nlog_n)\)

所以,我們就可以用\(O(1)\)的時間判斷出一個元素是不是只出現一次了。

若從左到右掃描整個序列,那麼最壞情況,這個元素在序列的最右邊,則\(Time(n)=Time(n-1)+O(n) \ge Time(n^2)=O(n^2)\)

根據二分法(分治)一般是盡量分成兩個數量盡量接近的數列,我們可以考慮從兩邊往中間找。

此時,最壞情況為這個元素在序列的正中間,則\(Time(n)=2\times Time(n/2)+O(n)\),解得\(Time(n)=O(nlog_n)\)

所以演算法的總時間複雜度為\(O(nlog_n)\)

#include <bits/stdc++.h>
using namespace std;
int s[200010];
int last[200010];
int nxt[200010];
map<int,int>be;
map<int,int>wi;
inline bool solve(int l,int r){
    if(l>=r)
    	return 1;
    int x=l,y=r; 
    while(x<=y){
        if(last[x]<l&&nxt[x]>r)
            return solve(l,x-1)&&solve(x+1,r);
        else if(last[y]<l&&nxt[y]>r)
                return solve(l,y-1)&&solve(y+1,r);
        x++,y--;
    }
    return 0;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--) {
        int n;
        scanf("%d",&n);
        be.clear();
        wi.clear();
        for(int i=1;i<=n;i++) {
            scanf("%d",&s[i]);
            if(!be.count(s[i]))last[i]=-1;
            else last[i]=be[s[i]];
            be[s[i]]=i;
        }
        for(int i=n;i>0;i--) {
            if(!wi.count(s[i]))nxt[i]=n+1;
            else nxt[i]=wi[s[i]];
            wi[s[i]]=i;
        }
        if(solve(1,n))
        	printf("non-boring");
        else
        	printf("boring");
    }   
    return 0;
}