题目:

BZOJ4059

分析:

想了半天没什么想法,百度到一个神仙做法……

设原数列为 \(a\),对于每一个 \(i\) 求出前一个和后一个和 \(a_i\) 相等的位置 \(pre[i]\) 和 \(nxt[i]\) (如果不存在则分别为 \(-1\) 和 \(n+1\) )。那么如果在一个区间 \([l, r]\) 中存在一个 \(i\) 满足 \(pre[i]<l\) 且 \(nxt[i]>r\) ,说明 \(i\) 在 \([l, r]\) 中只出现了一次,那么任意一个跨越 \(i\) 的 \([l, r]\) 的子区间都是不无聊的,只需要再判断 \([l, i)\) 和 \((i, r]\) 中是否存在这样的 \(i\) 即可。

\(i\) 从两边往中间暴力找(绝对不能从左往右暴力扫一遍!!,那样复杂度就高了),复杂度是 \(O(n\log n)
\)没想到吧!。

下面来证明(感性理解)一下这个神奇的复杂度。正着想比较麻烦,我们倒过来。把递归调用的树画出来(很显然是个二叉树),然后从下往上看,把分治倒过来变成向上合并。每次合并是一个长为 \(l_1\) 的区间和一个长为 \(l_2\) 的区间合起来变成一个长为 \(l_1+l_2+1\) 的区间。而这次合并所花的时间是从两端点暴力找 \(i\) 的时间,即 \(min(l_1, l_2)\) (当然还要乘个 \(2\) 之类,但那不影响复杂度)。这个过程是不是很像启发式合并?于是时间复杂度 \(O(n\log n)\)

代码:

思路清晰,解法自然

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-');
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
inline void write(const char *const s)
{
printf("%s", s);
}
const int N = 2e5 + 10;
int T, n, arr[N], tmp[N], last[N], nxt[N], pre[N];
bool check(const int l, const int r)
{
if (l > r)
return true;
int tmpl = l, tmpr = r;
for (int i = 0; i < (r - l + 1); i++)
if (i & 1)
{
if (pre[tmpl] < l && nxt[tmpr] > r)
return check(l, tmpl - 1) && check(tmpl + 1, r);
++tmpl;
}
else
{
if (pre[tmpr] < l && nxt[tmpr] > r)
return check(l, tmpr - 1) && check(tmpr + 1, r);
--tmpr;
}
return false;
}
int work()
{
read(T);
while (T--)
{
read(n);
for (int i = 0; i < n; i++)
read(arr[i]), tmp[i] = arr[i];
sort(tmp, tmp + n);
int cnt = unique(tmp, tmp + n) - tmp;
for (int i = 0; i < n; i++)
arr[i] = lower_bound(tmp, tmp + cnt, arr[i]) - tmp;
memset(last, -1, sizeof(int[cnt]));
for (int i = 0; i < n; i++)
{
if (~last[arr[i]])
nxt[last[arr[i]]] = i;
pre[i] = last[arr[i]];
last[arr[i]] = i;
}
for (int i = 0; i < cnt; i++)
nxt[last[i]] = n + 1;
if (check(0, n - 1))
write("non-boring\n");
else
write("boring\n");
}
return 0;
}
}
int main()
{
return zyt::work();
}

【BZOJ4059】Non-boring sequences(分析时间复杂度)的更多相关文章

  1. 【启发式拆分】bzoj4059: [Cerc2012]Non-boring sequences

    这个做法名字是从武爷爷那里看到的…… Description 我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短.一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子 ...

  2. 【BZOJ-4059】Non-boring sequences 线段树 + 扫描线 (正解暴力)

    4059: [Cerc2012]Non-boring sequences Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 440  Solved: 16 ...

  3. 【BZOJ4059】Non-boring sequences

    Solution 记序列为\(a\),计算出与\(a_i\)相等的前一个元素的位置\(pre_i\),以及后一个元素的位置\(nex_i\),显然,对于那些左端点处于\((pre_i,i]\)以及右端 ...

  4. bzoj4059 [Cerc2012]Non-boring sequences && bzoj5200 [NWERC2017]Factor-Free Tree

    https://konnyakuxzy.github.io/BZPRO/JudgeOnline/4059.html https://cn.vjudge.net/problem/Gym-100624D ...

  5. bzoj4059 [Cerc2012]Non-boring sequences

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4059 [题解] 考虑分治.定义过程solve(l,r)为判断全在[l,r]范围内的所有连续子 ...

  6. BZOJ4059[Cerc2012]Non-boring sequences(扫描线/分治)

    这题正解应该是扫描线,就是发现DP的区间在两个维度都为连续段,于是可以直接扫描线.但不幸的是,扫描线常数过大,无法通过本题. 考虑分治.对于分治区间[l,r],可以记录pre和nxt表示其前/后一次出 ...

  7. 【algo&ds】1.时间复杂度和空间复杂度分析

    1.时间复杂度分析O(f(n)) 分析方法 只关注循环执行次数最多的一段代码 加法原则 乘法原则 高优先级原则 常见时间复杂度量级 多项式量级和非多项式量级.其中,非多项式量级只有两个:O(2^n) ...

  8. 算法初级面试题01——认识时间复杂度、对数器、 master公式计算时间复杂度、小和问题和逆序对问题

    虽然以前学过,再次回顾还是有别样的收获~ 认识时间复杂度 常数时间的操作:一个操作如果和数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作. 时间复杂度为一个算法流程中,常数操作数量的指标.常 ...

  9. 维诺图(Voronoi Diagram)分析与实现(转)

    一.问题描述1.Voronoi图的定义又叫泰森多边形或Dirichlet图,它是由一组由连接两邻点直线的垂直平分线组成的连续多边形组成. 2.Voronoi图的特点(1)每个V多边形内有一个生成元: ...

随机推荐

  1. cdq分治入门--BZOJ1176: [Balkan2007]Mokia

    对w*w,w<=2000000的矩形,一开始全是0(或一开始全是s),n<=170000个操作,每次操作:矩阵内某点加上一个数,查某一个子矩阵的和,保证修改数<=160000,询问数 ...

  2. kibana启动--nohup在关闭终端后无效&&守护进程详解

    https://blog.csdn.net/ty_0930/article/details/70184705 https://blog.csdn.net/Dream_Flying_BJ/article ...

  3. Codeforces 629D Babaei and Birthday Cake(线段树优化dp)

    题意: n个蛋糕编号从小到大编号,j号蛋糕可以放在i号上面,当且仅当j的体积严格大于i且i<j,问最终可得的最大蛋糕体积. 分析: 实质为求最长上升子序列问题,设dp[i]从头开始到第i位的最长 ...

  4. 洛谷—— P2543 [AHOI2004]奇怪的字符串

    P2543 [AHOI2004]奇怪的字符串 题目描述 输入输出格式 输入格式: 输入文件中包含两个字符串X和Y.当中两字符串非0即1.序列长度均小于9999. 输出格式: X和Y的最长公共子序列长度 ...

  5. ip addr

    ip 32 位.四个字节.IP地址分为五类,A类保留给政府机构,B类分配给中等规模的公司,C类分配给任何需要的人,D类用于组播,E类用于实验,各类可容纳的地址数目不同.A.B.C三类IP地址的特征:当 ...

  6. windows 7 文件加密设置

    方法/步骤1 加密文件 1 右击需加密的文件,选择“属性”命令. 2 在属性对话框的“常规‘选项卡中单击”高级“按钮. 3 在弹出的对话框中选中”加密内容以便保护数据“复选框,单击”确定“按钮. 4 ...

  7. C# .NET如何清空stringbuilder

    就红色的代码可以:   System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("hello&qu ...

  8. 关于Windows 8使用WMP播放音乐时WUDFHost跑CPU和硬盘的问题解决

    Windows 8使用Windows Media Player播放音乐的时候.事实上有一个这种情况,WMP和某个什么名字看起来非常屌的进程跑CPU非常高,这个跑非常高视你插入的SD卡内的文件数或者移动 ...

  9. Android基础新手教程——4.1.1 Activity初学乍练

    Android基础新手教程--4.1.1 Activity初学乍练 标签(空格分隔): Android基础新手教程 本节引言: 本节開始解说Android的四大组件之中的一个的Activity(活动) ...

  10. Python开发【第5节】【函数基础】

    1.函数 函数的本质就是功能的封装. 函数的作用 提升代码的重复利用率,避免重复开发相同代码 提高程序开发效率 便于程序维护 2.函数定义 def 函数名(参数): """ ...