今天比赛的时候做的一个坑题。深坑啊。
题目意思是给你一个有n个数的数字序列。要你判断对于这个序列是都满足任意一个子序列都至少含有一个只出现一次的数字。
看完题目后没什么思路,一直以为要用线段树,每次删除一个点,然后更新,后来发现wa了,算法有问题。
后来殷犇讲了一种水过去的方法,听完后瞬间就水过了。
其实我们可以这样去做,对于数组中的每个数,我们建立一个指针指向这个数字下一次在数组中出现的位置。
然后核心的水的地方来了,如果我们已知一个区间中存在某一个数字只出现一次,假设区间为[l,r],这个只出现一次的数字的位置为x,那么我们只要去验证[l,x-1]和[x+1,r]是否分别满足即可。
同时为了水得更加有质量,我们在扫描的时候我们可以进行一点点优化,就是每次左边扫一个数,右边扫一个数,这样就过了噢。
其实我们可以论议论时间复杂度。当那个单独出现的数字靠近两边的时候我们可以迅速地找出来;当那个单独出现的数字出现在越中间,我们能把区间更加平分,这样时间也能降低下来哦。
总的来说今天还是学到了一种很好的水题的方法。orz!。
下面上代码:
Vie
#include <iostream> #include <cstdio> #include <cstring> #include <map> #define maxn 200200 using namespace std; int n1[maxn],n2[maxn],a[maxn],n,m,t; map<int,int> ss; bool check(int l,int r) { if (l>=r) return true; for (int i=0; i<=(r-l+1)/2; i++) { if (n1[l+i]>r && n2[l+i]<l) return check(l,l+i-1)&check(l+i+1,r); if (n1[r-i]>r && n2[r-i]<l) return check(l,r-i-1)&check(r-i+1,r); } return false; } int main() { scanf("%d",&t); while (t--) { scanf("%d",&n); for (int i=1;i<=n; i++) scanf("%d",&a[i]),n1[i]=n+1,n2[i]=0; ss.clear(); for (int i=1; i<=n; i++) { n2[i]=ss[a[i]],n1[n2[i]]=i; ss[a[i]]=i; } if (check(1,n)) printf("non-boring "); else printf("boring "); } return 0; }