我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短。一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次。给定一个整数序列,请你判断它是不是不无聊的。
我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短。一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次。给定一个整数序列,请你判断它是不是不无聊的。
第一行一个正整数T,表示有T组数据。每组数据第一行一个正整数n,表示序列的长度,1 <= n <= 200000。接下来一行n个不超过10^9的非负整数,表示这个序列。
对于每组数据输出一行,输出"non-boring"表示这个序列不无聊,输出"boring"表示这个序列无聊。
鸣谢Tjz
分治思路好题
其实这道题的方法是很暴力的,但是我们可以证明复杂度是O(nlogn)。每次判断一个区间[l,r]是否满足题意,我们从其中找到一个位置x,满足l<=x<=r且a[x]在[l,r]内只出现一次,然后递归判断[l,x-1]和[x+1,r]是否满足题意。如果不存在x则不满足题意。
如果x是从左到右找,复杂度是O(n^2)。
而x的寻找过程可以进行适当的优化。
如果x从两边向中间找,复杂度是O(nlogn)。将一个长度为n的区间分为两段长度为k和n-k的区间,则这次操作对于复杂度的贡献是O(min(k,n-k))。所以每次分治中,两段中较短的一段每一个位置对复杂度产生O(1)贡献,它的长度也至少缩短为原来一半。类似于启发式合并,每个位置对复杂度贡献是小于O(logn)的,整体复杂度为O(nlogn)。
#include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<map> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 200005 using namespace std; int t,n,a[maxn],pre[maxn],nxt[maxn]; map<int,int> mp; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline bool judge(int L,int R) { if (L>=R) return true; int l=L,r=R; F(i,L,R) { if (i&1) { if (pre[l]<L&&nxt[l]>R) return judge(L,l-1)&&judge(l+1,R); l++; } else { if (pre[r]<L&&nxt[r]>R) return judge(L,r-1)&&judge(r+1,R); r--; } } return false; } int main() { t=read(); while (t--) { n=read(); mp.clear(); F(i,1,n) { a[i]=read(); pre[i]=mp[a[i]];nxt[mp[a[i]]]=i; mp[a[i]]=i; } F(i,1,n) nxt[mp[a[i]]]=n+1; if (judge(1,n)) puts("non-boring"); else puts("boring"); } }