链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4483
题意:
如果一个序列的任意连续子序列中至少有一个只出现一次的元素,则称这个序列是不无聊(non-boring)的。
输入一个n(n≤200000)个元素的序列A(各个元素均为1e9以内的非负整数),判断它是不是不无聊的。
分析:
在整个序列中找一个只出现一次的元素,如果不存在,则这个序列不是不无聊的;
如果找到一个只出现一次的元素A[k],则只需检查A[1…k-1]和A[k+1…n]是否满足条件(分治)。
设长度为n的序列需要T(n)时间,则有T(n) = max{T(k-1) + T(n-k) + 找到唯一元素k的时间}。
如何找唯一元素?如果事先算出每个元素左边和右边最近的相同元素,
则可以在O(1)时间内判断在任意一个连续子序列中,某个元素是否唯一。
如果从左边找,最坏情况下唯一元素是最后一个元素,因此
T(n) = T(n-1) + O(n)≥T(n) = O(n*n)
从右往左找也一样,只不过最坏情况变成了“唯一元素是第一个元素”,但时间复杂度不变。
那么,从两边往中间找会怎样?此时T(n) = max{T(k) + T(n-k) + min(k,n-k)},
而此时的最坏情况是唯一元素在中间的情况,它满足经典递推式T(n) = 2T(n/2) + O(n),即T(n)=O(nlogn)。
代码:
1 #include <cstdio> 2 #include <map> 3 using namespace std; 4 5 const int UP = 200000 + 5; 6 int a[UP], prior[UP], after[UP]; 7 8 inline bool check(int p, int L, int R){ 9 return prior[p] < L && after[p] > R; 10 } 11 12 bool judge(int L, int R){ 13 if(L >= R) return true; 14 for(int d = 0; L + d <= R - d; d++){ 15 if(check(L+d, L, R)) return judge(L, L+d-1) && judge(L+d+1, R); 16 if(L + d == R - d) break; 17 if(check(R-d, L, R)) return judge(R-d+1, R) && judge(L, R-d-1); 18 } 19 return false; 20 } 21 22 int main(){ 23 int T, n; 24 scanf("%d", &T); 25 while(T--){ 26 map<int,int> M; 27 scanf("%d", &n); 28 for(int i = 0; i < n; i++){ 29 scanf("%d", &a[i]); 30 prior[i] = M.count(a[i]) ? M[a[i]] : -1; 31 M[a[i]] = i; 32 } 33 M.clear(); 34 for(int i = n - 1; i >= 0; i--){ 35 after[i] = M.count(a[i]) ? M[a[i]] : n; 36 M[a[i]] = i; 37 } 38 39 if(judge(0, n-1)) printf("non-boring "); 40 else printf("boring "); 41 } 42 return 0; 43 }