題解 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;
}