【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
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
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; }
| 欢迎来原网站坐坐! >原文链接<