@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 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
随机推荐
- 【python之路26】字符串之格式化%和format
Python基础之杂货铺 字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式, ...
- 一探前端开发中的JS调试技巧(转)
有请提示:文中涉及较多Gif演示动画,移动端请尽量在Wifi环境中阅读 前言:调试技巧,在任何一项技术研发中都可谓是必不可少的技能.掌握各种调试技巧,必定能在工作中起到事半功倍的效果.譬如,快速定位问 ...
- org.openqa.selenium.ElementNotInteractableException: element not interactable
F12查看元素确实存在的 有人说延长加载时间 webDriver.manage().timeouts().implicitlyWait(, TimeUnit.SECONDS); // 等待5秒加载完成 ...
- git diff 笔记
有一个 lab1 一个lab2 lab2 是比lab1 新的版本 但是Lab1 中有一些写的新代码,想要保留到lab2 中 直接使用patch 会把 lab2 回退到lab1 或lab1 更新到lab ...
- JQ效果 透明图片覆盖动画
效果图呈上 先说思路 1,一个固定的框架,有两张图片,一张是狗狗的,一张是练习方式,想把做好的练习方式隐藏 2,效果上想要从下面滑动出来,所以透明框定位在下面 3,整理需要的东西,缓慢升起需要动画效果 ...
- git之操作准则
每天下班前合一次代码,每次合代码先pull 不要多人同时修改同一个文件,避免冲突 在每个人自己的分支进行开发,先合并到dev分支解决冲突,确认无冲突后再合并到master
- npm ci命令比npm installer命令快2至10倍
npm 5.7.1的发布给我们带了一系列新的功能. 其中我最喜欢的就是npm ci命令了. npm ci命令 1.npm ci命令只根据lock-file去下载node_modules. 如果你的pa ...
- 光(mirror room)
/* 光线只有遇上边界或堵塞的格子才会改变方向,所以改变方向的位置是有限的,光线的方向又最多只有四种,所以光线在循环之前改变方向的次数是O(n+m+k)级别的.我们可以模拟光线的移动.已知光线位置和光 ...
- Eight HDU-1043 (bfs)
Eight Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Subm ...
- DirectX11笔记(二)--Direct3D初始化1之基本概念
原文:DirectX11笔记(二)--Direct3D初始化1之基本概念 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737/art ...