【BZOJ4059】[Cerc2012]Non-boring sequences 分治

【BZOJ4059】[Cerc2012]Non-boring sequences

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

题解:首先容易想到线段树做法,由于每个子串都是某个后缀的前缀,所以我们枚举以每个数为结尾的前缀。将这个数的权值设为1,将这个数的前驱的权值设为-1,前驱的前驱设为0,那么如果这个序列是boring的当且仅当该前缀的某个后缀权值和为0。线段树搞一搞就行。

然后又去学了分治做法。首先[l,r]中一定要有一个只出现过一次的数,否则无解。那么所有包含这个数的区间都是不无聊的,我们只需要递归处理左右两边即可。问题是我们怎么找到这个只出现过一次的数。先预处理出前驱和后继,一个数只出现一次等价于前驱<l且后继>r。由于我们的分治不是二分,所以我们每次查找的复杂度应该是余下两个区间长度的较小值。所以我们从两端往中间一个一个找就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=200010;
int flag,n;
struct node
{
	int val,org;
}num[maxn];
int pre[maxn],nxt[maxn];
bool cmp(node a,node b)
{
	return (a.val==b.val)?(a.org<b.org):(a.val<b.val);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
void solve(int l,int r)
{
	if(l>r)	return ;
	int mid,i;
	for(i=0;l+i<=r-i;i++)
	{
		if(pre[l+i]<l&&nxt[l+i]>r)
		{
			mid=l+i;
			break;
		}
		if(pre[r-i]<l&&nxt[r-i]>r)
		{
			mid=r-i;
			break;
		}
	}
	if(l+i>r-i)	flag=1;
	if(!flag)	solve(l,mid-1);
	if(!flag)	solve(mid+1,r);
}
void work()
{
	n=rd();
	int i;
	for(i=1;i<=n;i++)	num[i].val=rd(),num[i].org=i,pre[i]=0,nxt[i]=n+1;
	sort(num+1,num+n+1,cmp);
	for(i=1;i<=n;i++)	if(num[i].val==num[i-1].val)	pre[num[i].org]=num[i-1].org,nxt[num[i-1].org]=num[i].org;
	flag=0,solve(1,n);
	if(flag)	printf("boring\n");
	else	printf("non-boring\n");
}
int main()
{
	int T=rd();
	while(T--)	work();
	return 0;
}
posted @ 2017-08-24 08:31  CQzhangyu  阅读(232)  评论(0编辑  收藏  举报