UVA - 1608 Non-boring sequences (分治,中途相遇法)
如果一个序列中是否存在一段连续子序列中的每个元素在该子序列中都出现了至少两次,那么这个序列是无聊的,反正则不无聊。给你一个长度为n(n<=200000)的序列,判断这个序列是否无聊。
稀里糊涂AC的一道题。
如果一个序列不无聊,那么一定至少存在一个独一无二的元素。如果找到了该元素,那么只需分别判断该元素两侧的子序列是否无聊即可。找到一个独一无二的元素的期望时间复杂度为$O(n)$(n为当前序列长度),显然如果该独一无二的元素在中间的话,则复杂度$T(n)=2T(n/2)+O(n)=O(nlogn)$,但假如比较靠边的话,情况就有些尴尬了,最坏情况下复杂度为$O(n^2)$。解决方法是从两边向中间找,这样的话复杂度就成了$T(n)=max\{T(k)+T(n-k)+min(k,n-k)\}$,比较玄学,据说是$O(nlogn)$的,但我不会证明QAQ,而且不知道为啥一样的代码逻辑,很多都跑了100+ms,而我的却跑了2500+ms,勉强过掉...
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 const int N=2e5+10; 6 int n,a[N],b[N],c[N],nn,pre[N],nxt[N],last[N]; 7 8 void disc(int* a,int n) { 9 for(int i=0; i<n; ++i)b[i]=a[i]; 10 sort(b,b+n); 11 nn=unique(b,b+n)-b; 12 for(int i=0; i<n; ++i)a[i]=lower_bound(b,b+nn,a[i])-b; 13 } 14 15 int uni(int i,int l,int r) {return pre[i]<l&&nxt[i]>r;} 16 17 int solve(int l,int r) { 18 if(l>=r)return 1; 19 int mid=-1; 20 for(int L=l,R=r; L<=R; ++L,--R) { 21 if(uni(L,l,r)) {mid=L; break;} 22 if(uni(R,l,r)) {mid=R; break;} 23 } 24 if(mid==-1)return 0; 25 return solve(l,mid-1)&&solve(mid+1,r); 26 } 27 28 int main() { 29 int T; 30 for(scanf("%d",&T); T--;) { 31 scanf("%d",&n); 32 for(int i=0; i<n; ++i)scanf("%d",&a[i]); 33 disc(a,n); 34 memset(last,-1,sizeof last); 35 for(int i=0; i<n; ++i) { 36 pre[i]=last[a[i]],last[a[i]]=i,nxt[i]=n; 37 if(~pre[i])nxt[pre[i]]=i; 38 } 39 puts(solve(0,n-1)?"non-boring":"boring"); 40 } 41 return 0; 42 }