题目描述
题目大意:被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次。给定一个整数序列,请你判断它是不是不无聊的。
题解
首先一个扫描线(树套树)的做法
对于每一个点i,假设前面和后面和它相同的数字的位置为pre和nxt,那么左端点在[pre+1,i],右端点在[i,nxt-1]中的子区间都是合法的。将[pre+1,i]看成横坐标,[i,nxt-1]看成纵坐标,可以在坐标系中抽象出来一个矩形,然后用扫描线判断一下这n个矩形是否能将一个上三角覆盖就行了
但是被卡得死T活T。。线段树常数确实大
然后就学习了一下一个网上的分治做法
大概就是判断一个区间[l,r]是否是合法的,只需要在[l,r]中找到一个唯一出现的点p,然后判断[l,p-1]和[p+1,r]是否是合法的就行了,这就相当于是一个分治;但是如何找到p呢?对于某一个点判断是
O(1)
的,但是枚举的时候从两边向中间找,这样时间复杂度就是
O(nlogn)
的了
好像是唐教给出的证明吧?网上还有一个我认为很不错的直观证明:http://whx991201.is-programmer.com/posts/190036.html感觉很有趣
代码
扫描线tle
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 200005
int T,n,LSH,cnt;
int a[N],b[N],lsh[N],pre[N],nxt[N],head[N],tail[N];
struct data{int x,l,r,val;}line[N<<1];
int sum[N*4],cover[N*4];
bool flag;
void clear()
{
n=LSH=cnt=0;
memset(sum,0,sizeof(sum));memset(cover,0,sizeof(cover));
}
int cmp(data a,data b) {return a.x<b.x;}
void update(int now,int l,int r)
{
if (cover[now]) sum[now]=r-l+1;
else if (l==r) sum[now]=0;
else sum[now]=sum[now<<1]+sum[now<<1|1];
}
void change(int now,int l,int r,int lr,int rr,int v)
{
if (lr>rr) return;
int mid=(l+r)>>1;
if (lr<=l&&r<=rr)
{
cover[now]+=v;update(now,l,r);
return;
}
if (lr<=mid) change(now<<1,l,mid,lr,rr,v);
if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,v);
update(now,l,r);
}
int cmplsh(int x,int y) {return b[x]<b[y];}
int main()
{
scanf("%d",&T);
while (T--)
{
clear();
scanf("%d",&n);b[0]=-1;
for (int i=1;i<=n;++i)
{
scanf("%d",&b[i]),lsh[i]=i;
pre[i]=nxt[i]=head[i]=tail[i]=0;
}
sort(lsh+1,lsh+n+1,cmplsh);
for (int i=1;i<=n;++i)
if (b[lsh[i]]!=b[lsh[i-1]]) a[lsh[i]]=++LSH;
else a[lsh[i]]=LSH;
for (int i=1;i<=n;++i)
{
pre[i]=tail[a[i]];
tail[a[i]]=i;
}
for (int i=n;i>=1;--i)
{
nxt[i]=head[a[i]];
head[a[i]]=i;
}
for (int i=1;i<=n;++i)
{
int l=pre[i]+1,r=(nxt[i])?nxt[i]-1:n;
line[++cnt].x=l-1,line[cnt].l=i-1,line[cnt].r=r,line[cnt].val=1;
line[++cnt].x=i,line[cnt].l=i-1,line[cnt].r=r,line[cnt].val=-1;
}
sort(line+1,line+cnt+1,cmp);
bool flag=1;
for (int i=1,j;i<=cnt;i=j)
{
j=i;
if (i>1&&sum[1]!=n-line[i].x+1){flag=0;break;}
while (j<=cnt&&line[j].x==line[i].x)
{
change(1,1,n,line[j].l+1,line[j].r,line[j].val);
++j;
}
}
if (flag) puts("non-boring");
else puts("boring");
}
}
暴力分治
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 200005
int T,n,LSH;
int a[N],b[N],lsh[N],pre[N],nxt[N],head[N],tail[N];
bool check(int l,int r)
{
if (l>=r) return 1;
int t[2]={l,r};
for (int p=0;t[0]<=t[1];(p^=1)?t[0]++:t[1]--)
if (pre[t[p]]<l&&nxt[t[p]]>r)
return check(l,t[p]-1)&&check(t[p]+1,r);
return 0;
}
int cmplsh(int x,int y){return b[x]<b[y];}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);b[0]=-1;
for (int i=1;i<=n;++i)
{
scanf("%d",&b[i]),lsh[i]=i;
pre[i]=tail[i]=0;nxt[i]=head[i]=n+1;
}
sort(lsh+1,lsh+n+1,cmplsh);LSH=0;
for (int i=1;i<=n;++i)
if (b[lsh[i]]!=b[lsh[i-1]]) a[lsh[i]]=++LSH;
else a[lsh[i]]=LSH;
for (int i=1;i<=n;++i)
{
pre[i]=tail[a[i]];
tail[a[i]]=i;
}
for (int i=n;i>=1;--i)
{
nxt[i]=head[a[i]];
head[a[i]]=i;
}
bool flag=check(1,n);
if (flag) puts("non-boring");
else puts("boring");
}
}