bzoj4059【Cerc2012】Non-boring sequences

4059: [Cerc2012]Non-boring sequences

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 397   Solved: 141
[ Submit][ Status][ Discuss]

Description

我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短。一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次。给定一个整数序列,请你判断它是不是不无聊的。

Input

第一行一个正整数T,表示有T组数据。每组数据第一行一个正整数n,表示序列的长度,1 <= n <= 200000。接下来一行n个不超过10^9的非负整数,表示这个序列。

Output

对于每组数据输出一行,输出"non-boring"表示这个序列不无聊,输出"boring"表示这个序列无聊。

Sample Input

4
5
1 2 3 4 5
5
1 1 1 1 1
5
1 2 3 2 1
5
1 1 2 1 1

Sample Output

non-boring
boring
non-boring
boring

HINT

Source

鸣谢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");
	}
}


你可能感兴趣的:(分治,bzoj)