@loj - 2507@ 「CEOI2011」Matching
@description@
对于整数序列 \((a_1, a_2, ..., a_n)\) 和 1 ~ n 的排列 \((p_1, p_2, ..., p_n)\),称 \((a_1, a_2, ..., a_n)\) 符合 \((p_1, p_2, ..., p_n)\),当且仅当:
(1){a} 中任意两个数字互不相同。
(2)将 \((a_1, a_2, ..., a_n)\) 从小到大排序后,将会得到 \((a_{p_1}, a_{p_2}, ..., a_{p_n})\)。
现在给出 1 ~ n 的排列 {p} 与序列 \(h_1, h_2, ..., h_m\),请你求出哪些 h 的子串符合排列 {p}。
输入格式
第一行两个空格隔开的正整数 n, m。
第二行 n 个空格隔开的正整数,表示排列 p。
第三行 m 个空格隔开的正整数,表示序列 h。
输出格式
第一行一个整数 k,表示符合 {p} 的子串个数。
第二行 k 个空格隔开的正整数,表示这些子串的起始位置(编号从 1 开始)。请将这些位置按照从小到大的顺序输出。特别地,若 k = 0,那么你也应当输出一个空行。
样例输入
5 10
2 1 5 3 4
5 6 3 8 12 7 1 10 11 9
样例输出
2
2 6
数据范围与提示
2 <= n <= m <= 1000000; 1 <= hi <= 10^9; 1 <= pi <= n。
且保证 {h} 中元素互不相同,{p} 是一个排列。
@solution@
先对问题作一步转化:求 {q} 使得 \(q_{p_i} = i\),即 {p} 的逆置换。
那么某个子串符合 {p} 可以等价于这个子串离散化到 1 ~ n 中后等于 q。
如果不考虑离散化,那么就是一个经典子串匹配问题,直接上 kmp。
假如子串同构的判定方法如上,即离散化后同构,是否还可以扩展一下 kmp 呢?
考虑 kmp 什么时候需要判同构:已知串 s 与串 t 同构时,在 s 末尾加一个 a,在 t 末尾加一个 b,判断 s + a 与 t + b 是否同构。
因为 s 与 t 已经同构了,只需要 a 与 b 加入进去过后仍然同构即可。
可以等价于判定 a 在 s 中的排名(s 中比 a 小的数) = b 在 t 中的排名(t 中比 b 小的数)。
查排名可以平衡树,不过这道题直接离散化 + 树状数组即可。
注意 kmp 在跳 fail 的时候,需要一个个元素的移动,因为要维护树状数组。
不过复杂度的证明是不会变的。kmp 还是 O(n),套个树状数组就是 O(nlogn) 的。
@accepted code@
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1000000;
int n, m;
int t[2][MAXN + 5];
int lowbit(int x) {return x & -x;}
void update(int x, int k, int type) {
for(int i=x;i<=m;i+=lowbit(i))
t[type][i] += k;
}
int sum(int x, int type) {
int ret = 0;
for(int i=x;i;i-=lowbit(i))
ret += t[type][i];
return ret;
}
int d[MAXN + 5], p[MAXN + 5], h[MAXN + 5];
void discrete() {
for(int i=1;i<=m;i++) d[i] = h[i];
sort(d + 1, d + m + 1);
for(int i=1;i<=m;i++)
h[i] = lower_bound(d + 1, d + m + 1, h[i]) - d;
}
int f[MAXN + 5];
void get_f() {
f[1] = 0;
int ri = 0, le = 2;
for(int i=2;i<=n;i++) {
int j = f[i-1];
while( sum(p[j+1], 0) != sum(p[i], 1) ) {
while( ri != f[j] )
update(p[ri--], -1, 0), update(p[le++], -1, 1);
j = f[j];
}
f[i] = j + 1;
update(p[++ri], 1, 0), update(p[i], 1, 1);
}
}
vector<int>ans;
void get_ans() {
for(int i=1;i<=m;i++)
t[0][i] = t[1][i] = 0;
int le = 1, ri = 0, j = 0;
for(int i=1;i<=m;i++) {
while( sum(p[j+1], 0) != sum(h[i], 1) ) {
while( ri != f[j] )
update(p[ri--], -1, 0), update(h[le++], -1, 1);
j = f[j];
}
j++;
update(p[++ri], 1, 0), update(h[i], 1, 1);
if( j == n ) {
ans.push_back(i-n+1);
while( ri != f[j] )
update(p[ri--], -1, 0), update(h[le++], -1, 1);
j = f[j];
}
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) {
int x; scanf("%d", &x);
p[x] = i;
}
for(int i=1;i<=m;i++) scanf("%d", &h[i]);
discrete(), get_f(), get_ans();
printf("%d\n", (int)ans.size());
for(int i=0;i<(int)ans.size();i++)
printf("%d%c", ans[i], (i + 1 == ans.size() ? '\n' : ' '));
if( ans.empty() ) puts("");
}
@details@
曾经想过字符串 hash,因为这个 hash 直接就是康托展开算。
发现这个题 hash 并不能前缀和相减,动态维护感觉还要写平衡树,于是放弃了。
毕竟相对于平衡树大家还是喜欢代码简短的树状数组吧。
@loj - 2507@ 「CEOI2011」Matching的更多相关文章
- 【LOJ#2507】[CEOI2011]Matching(KMP,树状数组)
[LOJ#2507][CEOI2011]Matching(KMP,树状数组) 题面 LOJ 题解 发现要做的是排名串的匹配. 然后我们考虑把它转成这个位置之前有多少个数小于当前这个数,这样子只要每个位 ...
- Loj #2192. 「SHOI2014」概率充电器
Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...
- Loj #3096. 「SNOI2019」数论
Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...
- Loj #3093. 「BJOI2019」光线
Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...
- Loj #3089. 「BJOI2019」奥术神杖
Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...
- Loj #2542. 「PKUWC2018」随机游走
Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...
- Loj #3059. 「HNOI2019」序列
Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
随机推荐
- Tomcat服务器的安装及配置
学习目标: 了解Tomcat服务器的主要作用 掌握Tomcat服务器的安装与配置 掌握Tomcat安装目录下主要文件夹的作用 jsp的执行流程 1.Web的工作原理流程图:从图中可以看出Tomcat服 ...
- 使用pdf.js在移动端预览pdf文档
pdf.js 是一个技术原型主要用于在 HTML5 平台上展示 PDF 文档,无需任何本地技术支持. 在线演示地址:http://mozilla.github.com/pdf.js/web/viewe ...
- scrollbar 滚动条
滚动条样式:自定义元素的滚动条 <!DOCTYPE html> <html> <head lang="en"> <meta charset ...
- 一次完整的HTTP事务的过程、从输入URL到网页展示,浏览器都经历了什么?
详细介绍:老生常谈-从输入url到页面展示到底发生了什么 (1)一次完整的HTTP事务的过程 基本流程: a. 域名解析 b. 发起TCP的3次握手 c. 建立TCP连接后发起http请求 d. 服务 ...
- 玩转webpack之webpack的entry output
webpack的入口配置项表示要配置的文件就是开发环境或者生产环境 浏览器本身不能认识的一些东西必须经过webpack的编译才能认识,但是要去写的时候我们经常用到预编译什么的比如scss比如jsx甚至 ...
- 20190815-$N \Theta IP$
$NOIP$ 请选择您想测试的难度: 「困难」 「困难的地狱」 「能被神犇切掉的」 「你做不出来的」 「简单(完成前面所有后解锁)」 要难死了-- 考试过程: 首先看看三道题: 这是NOIP模拟测试? ...
- Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---适配器模式之TurkeyAdapter[转]
适配器模式的主要意图是对现有类的接口进行转换,以满足目标类的需求.其次,可以给目标类的接口添加新的行为(主要指方法).这一点容易与装饰模式混淆.从意图方面来看,装饰模式不改变(通常指增加)接口中的行为 ...
- 分布式事务 XA 两段式事务 X/open CAP BASE 一次分清
分布式事务: 分布式事务是处理多节点上 的数据保持 类似传统 ACID 事物特性的 一种事物. XA:是一种协议,一种分布式事务的协议,核心思想是2段式提交. 1 准备阶段 2 提交阶段.XA协议是 ...
- CSS 教程 - 闭合浮动元素
按照CSS规范,浮动元素(floats)会被移出文档流,不会影响到块状盒子的布局而只会影响内联盒子(通常是文本)的排列. 因此当其高度超出包含容器时,一般父容器不会自动伸长以闭合浮动元素. 但是有时我 ...
- 【洛谷】P1888 三角函数
P1888 三角函数 题目描述 输入一组勾股数a,b,c(a≠b≠c),用分数格式输出其较小锐角的正弦值.(要求约分.) 输入输出格式 输入格式: 一行,包含三个数,即勾股数a,b,c(无大小顺序). ...