一. 题面:点这里

二. 思路:

首先需要感谢 良心WA题人 巨佬提供的复杂度证明思路,好人一生平安。

首先考虑答案的性质,发现答案具备单调性,所以考虑二分答案 \(m\) 。然后对于朴素的 check 你可以做到每一次都是 \(O(n)\) 的。然后枚举每一个记忆的起始数字,总复杂度 \(O(n^2\log n)\)。

考虑优化,显然枚举每一个记忆起始数字这个部分是每一次的答案贡献,我们很难优化,那么考虑 check 的部分,思考具体的过程。每一次 check 我们都是扫一遍,遇到最近的 \(x+1\) 就作为最后答案序列的一部分,这个贪心的正确性是显然的。然后你发现这就形成了一个跳跃的过程,那么我们考虑是否可以预处理维护。然后就发现这个其实是可以线性维护预处理的(细节看代码)。但是如果连续段很稠密,你发现复杂度还是会退化成朴素做法,但是因为形成了跳跃的过程,有经验的选手就会发现这显然是可以倍增的,所以搞一个倍增数组 \(f_{i,j}\) ,意义不再阐述,而 \(f_{i,0}\) 就是我们刚才预处理的东西。那么你发现我们现在就可以在 \(O(\log n)\) 的复杂度下去处理上述贪心了。所以一次 check 的复杂度就变成了 \(O(k\log n)\) 。具体的,你可以枚举每个起始值 \(x\) 的位置,然后暴力往后跳。你发现枚举起始值 \(x\) 的过程是均摊线性的。所以总时间复杂度由 枚举+二分+check 三部分组成,最终总时间复杂度为:\(O(n\log^2n)\) 。

到目前为止,我们已经可以拿到 82pts 了,但是还可以更加优化。注意到一个性质,若起始值为 \(x\) 的答案记作 \(ans\) ,那么起始值为 \(x+1\) 的答案至少为 \(\max\{0,ans-1\}\) ,所以我们考虑维护一个双指针,左端点为 \(x\) ,右端点为 \(x+ans-1\) 。然后再去利用上述的 check 去做,这样的话就省去了二分的过程,效率会更优化,但是我们希望可以量化这个复杂度(再次感谢巨佬 良心WA题人)。和第一次的优化相仿的,我们不妨采取均摊复杂度分析。考虑双指针维护的某一个区间 \([l,r]\) ,我们记他们分别在原序列中的 \(cnt\) 有一个比较显然的性质就是区间 \([l,r]\) 的出现次数一定不会超过 \(\min\{cnt_l,cnt_r\}\) 然后你发现双指针的本质就是我们按照区间 \([l,r]\) 的出现次数扫那么也是均摊线性的,所以本质上我们就去掉了二分答案中的那一层 \(\log\) 。

在最终实现上还有对代码运行速度的优化,所以建议读者阅读本人的代码以作参考。

三. Code:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch > '9' || ch < '0'){if(ch == '-'){f = -1;}ch = getchar();}
while(ch >= '0'&&ch <= '9'){x = x * 10 + ch - 48; ch = getchar();}
return x * f;
} void write(int x) {
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + 48);
} const int N = 2e6 + 10,LOGN = 24;
std::vector<int> pos[N];
int n,k,V,f[N][LOGN],tmp[N],a[N],Log[N]; int retn_pos(int id,int v)
{
if(id > n && v > V) return n + 1;
return *std::upper_bound(pos[v].begin(),pos[v].end(),id);
} int lowbit(int x){return x & (-x);}
bool check(int num,int m)
{
--m;
int ps = 0;
int tp[21];
ps = retn_pos(ps,num);
int cnt = 0,j;
while(m)
{
j = lowbit(m);
m -= j;
j = Log[j];
ps = f[ps][j];
tp[++cnt] = j;
if(ps == n + 1) return false;
}
if(ps == n + 1) return false; for(int i = 1;i <= k - 1;++i)
{
ps = retn_pos(ps,num);
for(int j = 1;j <= cnt;++j)
{
ps = f[ps][tp[j]];
if(ps == n + 1) return false;
}
if(ps == n + 1) return false;
}
return true;
} int main()
{
n = read(),k = read(),V = read();
for(int i = 2;i <= n;++i) Log[i] = Log[i >> 1] + 1;
for(int i = 1;i <= n;++i)
{
a[i] = read();
pos[a[i]].push_back(i);
}
for(int i = 1;i <= V;++i) pos[i].push_back(n + 1); for(int i = n;i >= 1;--i)
{
if(!tmp[a[i] + 1]) f[i][0] = n + 1;
else f[i][0] = tmp[a[i] + 1];
tmp[a[i]] = i;
} f[n + 1][0] = n + 1;
for(int j = 1;j <= 20;++j)
for(int i = 1;i <= n + 1;++i)
f[i][j] = f[f[i][j - 1]][j - 1]; int ans = 0;
for(int i = 1;i <= V;++i)
{
ans = std::max(0,ans - 1);
while(check(i,ans + 1)) ++ans;
write(ans);
putchar(32);
}
return 0;
}

P12008 【MX-X10-T4】[LSOT-4] Fragment of Memories 题解的更多相关文章

  1. 蓝书4.1-4.4 树状数组、RMQ问题、线段树、倍增求LCA

    这章的数据结构题很真实 T1 排队 bzoj 1699 题目大意: 求静态一些区间的最大值-最小值 思路: ST表裸题 #include<iostream> #include<cst ...

  2. 2021.8.19考试总结[NOIP模拟44]

    T1 emotional flutter 把脚长合到黑条中. 每个黑条可以映射到统一区间,实际操作就是左右端点取模.长度大于$k$时显然不合法. 然后检查一遍区间内有没有不被黑条覆盖的点即可. 区间端 ...

  3. [NOIP2014]自测

    这两天做完了2014年的noip提高. 因为以前看了SDSC2016时gty的课件,题目思路都知道了一点,做起来没多大困难. 100+100+75+100+100+70=545 里面水分好多,好多题都 ...

  4. 算法之DP

    一般DP 都是有模板的,先初始化,然后找到不同状态下数值的关系,使得某个状态可用另一个状态由一个固定的方式转移而来,列出状态转移方程,这就是DP: 例题 P1216 [USACO1.5]数字三角形 N ...

  5. Noip模拟55 2021.9.17(打表大胜利)

    T1 skip 普通$dp$很好打: $f[i]=max(f[j]-\sum_{k=1}^{K}k+a_i)$ 就是要注意边界问题很烦人. 1 #include<bits/stdc++.h> ...

  6. 南昌航空大学-软件学院-22206104-段清如-JAVA第一次Blog作业

    南昌航空大学-软件学院-22206104-段清如-JAVA第一次Blog作业 前言: 这个学期才开始接触java,到现在一个多月的时间,已经差不多可以写出一些基本的简单的程序了.对比上个学期学习的C语 ...

  7. EventBus框架在Android多Pane(Fragment)中的应用

    通常多pane的设计中,比如Fragment A是个Headline,Fragement B是detail,那么B通常需要实现A的点击事件的接口,这样子的话,两个fragment就高耦合了,而且需要在 ...

  8. ViewPager,使用Fragment实现

    效果如图: 使用Fragment实现tab的缺点就是不能够滑动.不过应该也算优点,具体场景可以自由选择. 完整代码:imooc-tab022fragment,在我的百度云网盘上. MainAcgtiv ...

  9. 【佛山市选2013】JZOJ2020年8月7日T4 排列

    [佛山市选2013]JZOJ2020年8月7日T4 排列 题目 描述 一个关于n个元素的排列是指一个从{1, 2, -, n}到{1, 2, -, n}的一一映射的函数.这个排列p的秩是指最小的k,使 ...

  10. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

随机推荐

  1. VMware NSX Manager SSL证书更新

    安装 NSX 后,管理器节点和集群具有自签名证书.证书有效期为825天,到期后需要进行证书重新更新.如图所示,本环境中此次将有三个类型的证书即将到期需要替换:1.NSX 联合身份验证 PI(Local ...

  2. 聊一聊 .NET Dump 中的 Linux信号机制

    一:背景 1. 讲故事 当 .NET程序 在Linux上崩溃时,我们可以配置一些参考拿到对应程序的core文件,拿到core文件后用windbg打开,往往会看到这样的一句信息 Signal SIGAB ...

  3. 图扑软件 | 带你体验 Low Poly 卡通三维世界

    在三维场景搭建中,图扑软件提供了多样化的设计风格,以满足不同项目的视觉需求.无论是写实风格的细腻渲染.科幻未来的赛博质感,还是简约现代的几何美学,都能通过灵活的工具体系实现.而今天,我们将重点介绍一种 ...

  4. MySQL中用户及权限管理(mysql8.0版本)

    概述 在MySQL中,用户与权限管理属于关键的安全机制,能让你对数据库的访问进行精准控制 MySQL用户管理 创建用户信息 语法 CREATE USER username@'host' IDENTIF ...

  5. 【中英】【吴恩达课后测验】Course 5 -序列模型 - 第二周测验 - 自然语言处理与词嵌入

    [中英][吴恩达课后测验]Course 5 -序列模型 - 第二周测验 - 自然语言处理与词嵌入 上一篇:[课程5 - 第一周编程作业]※※※※※ [回到目录]※※※※※下一篇:[课程5 -第二周编程 ...

  6. GoWeb服务器搭建

    GoWeb服务器的创建 1.Web工作原理 2.GoWeb服务器的创建 Go提供了一系列用于创建Web服务器的标准库,而且通过Go创建一个服务器的步骤非常简单,只要通过net/http包调用Liste ...

  7. 现代 Python 包管理器 `uv`

    用 uv + Python 开发命令行工具 当使用 uv 写正规一点的 CLI 应用的时候,还是应该使用 uv init --package [package name] 因为写一个命令行程序总是要安 ...

  8. 打工人神助攻!2025年最火OKR工具榜单,看板式目标管理必备

    OKR(目标与关键成果)已成为企业目标管理的标准框架,为确保OKR目标可落地,可视化.协作性强的工具需求激增.2025年,哪些OKR工具真正助力团队落地战略?本文盘点当下最火的5款OKR工具,帮助HR ...

  9. C# WinForms 实现打印监听组件

    一.组件简介 打印监听组件是一款集成于 Windows 桌面环境的打印任务管理与监控工具,适用于企业级应用场景.它不仅支持多打印机任务的实时监控,还能通过 WebSocket 与外部系统集成,实现自动 ...

  10. vue3 + springboot实现微信登录

    创建VUE3项目 创建初始文件 进入项目存放位置 右键用命令行打开(终端打开) npm create vite@latest wechat-report --template vue npm:包管理需 ...