前言

题目链接:洛谷

时间复杂度和输入同阶的做法。

题意简述

有 \(n\)(\(n \leq 10^3\))个长 \(m\) 的字符串,\(q\)(\(q \leq 10^5\))次操作,交换两个字符串的两个字符。问每个字符串在所有时刻,最多有几个和它相等。

题目分析

套路做法

看到字符串相等,想到使用哈希。但是要支持修改,怎么办呢?上数据结构! 当然不是,把哈希的结果看做一个数字,每次把某一位的值扣掉再加上就行了。即减去该位权值乘上原值,加上权值乘上修改后的值。

至于询问,每次操作后不可能 \(\Theta(n^2)\) 地弄。发现只会和当前修改的两个串有关,故 \(\Theta(n)\) 把这两个串的结果统计一遍。其他的串,维护一个 \(cur_i\) 表示当前 \(i\) 能和几个字符串相等。如果 \(i\) 和修改之前的串相等,就减去,如果和修改后的串相等了,再加上,最后和答案取个 \(\max\) 即可。

需要注意的是,当两个字符串是同一个的时候,可能需要小小特判。

时间复杂度 \(\Theta(nm + nq)\)。可过此题。当然,可以优化。

优化做法

尝试从哈希表角度分析。发现,一个字符串不断从一个相同哈希值的集合中,移动到另一个集合中,而答案就是这段时间内,该集合大小的最大值。如果考虑在删除的时候统计答案,就是在该元素加入集合后,该集合大小的最大值。

尝试把集合大小独立出来,看做单独的一个权值。我们可以把元素扩展,额外附加它在加入集合时集合的大小,发现这样删除操作就没有了,而是直接将所在集合那个附加的权值减一。这样查询,就成了一个后缀最大值。即从所在结点一直扫到哈希表末尾,取每个结点权值的最大值,得到这段时间该集合大小的最大值。

对于一条链,我们会扫过很多次,不妨从这里优化,即去除重复操作。想到,如果扫过了一个值,那下次不用再从原来的位置开始再扫一遍,而是把当前扫过来的最值记下来,下次直接从这里开始扫就行了。很类似并查集的路径压缩。

时间复杂度呢?每条边只会被经过一遍,和 \(n + q\) 是同阶的。总的时间复杂度是 \(\Theta(nm + q)\) 的,非常快。

代码

naive code

代码

优化

哈希值域太小会 WA,所以使用了 unordered_map,对时间复杂度没有影响。略去了快读,是最优解

#include <cstdio>
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std; const int N = 1010;
const int M = 110;
const int Q = 100010;
const int mod = 1e9 + 11;
const int bas = 1331; inline int add(int a, int b) {
return a + b >= mod ? a + b - mod : a + b;
} inline int sub(int a, int b) {
return a - b < 0 ? a - b + mod : a - b;
} inline int mul(int a, int b) {
return 1ll * a * b % mod;
} int n, m, q;
char str[N][M];
int hsh[N], res[N], pw[M];
int whr[N]; // 记录字符串对应哪一个结点 struct node {
int fa, ans;
} tree[N + Q * 2];
int tot; unordered_map<int, int> ysiz, ylst; // 哈希对应的字符串有几个、上一个哈希值为它是哪个结点 int query(int x) { // 返回后缀最大值,并路径压缩
if (tree[x].fa == x) return tree[x].ans;
int res = query(tree[x].fa);
tree[x].fa = tree[tree[x].fa].fa;
return tree[x].ans = max(tree[x].ans, res);
} void update(int x) {
int bl = hsh[x];
whr[x] = ++tot, ++ysiz[bl];
tree[tot] = {tot, ysiz[bl]};
tree[ylst[bl]].fa = tot;
ylst[bl] = tot;
} signed main() {
fread(buf, 1, MAX, stdin);
read(n), read(m), read(q);
pw[0] = 1;
for (int i = 1; i <= m; ++i)
pw[i] = mul(pw[i - 1], bas);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
hsh[i] = add(mul(hsh[i], bas), str[i][j] = readchar());
}
update(i);
}
for (int i = 1, a, x, b, y; i <= q; ++i) {
read(a), read(x), read(b), read(y);
res[a] = max(res[a], query(whr[a])), --ysiz[hsh[a]];
if (a != b) res[b] = max(res[b], query(whr[b])), --ysiz[hsh[b]];
hsh[a] = sub(hsh[a], mul(pw[m - x], str[a][x]));
hsh[b] = sub(hsh[b], mul(pw[m - y], str[b][y]));
swap(str[a][x], str[b][y]);
hsh[a] = add(hsh[a], mul(pw[m - x], str[a][x]));
hsh[b] = add(hsh[b], mul(pw[m - y], str[b][y]));
update(a);
if (a != b) update(b);
}
for (int i = 1; i <= n; ++i) {
printf("%d\n", max(res[i], query(whr[i]))); // 这里注意也要取 max
}
return 0;
}

[POI2008] POC-Trains 题解的更多相关文章

  1. 【BZOJ1125】[POI2008]Poc hash+map+SBT

    [BZOJ1125][POI2008]Poc Description n列火车,每条有l节车厢.每节车厢有一种颜色(用小写字母表示).有m次车厢交换操作.求:对于每列火车,在交换车厢的某个时刻,与其颜 ...

  2. [POI2008]枪战Maf题解

    问题 C: [POI2008]枪战Maf 时间限制: 1 Sec  内存限制: 256 MB 题目描述 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺 ...

  3. 【luogu P3469 [POI2008]BLO-Blockade】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3469 #include <cstdio> #include <cstring> #i ...

  4. [POI2008] Poc (原名 Trians) Treap+Hash

    这个题和千山鸟飞绝体现出了一种用平衡树解决动态集合问题,主要套路就是蜜汁标记. 这个题我一开始用替罪羊树搞了一下对了28个点,后来我换成了Treap一搞对了14个点,再后来发现被卡了Hash我竟然在自 ...

  5. bzoj1125:[POI2008]Poc

    传送门 这个题好难卡啊. 看到这种题自然会想到字符串hash是不是,但是对于每次操作造成的影响需要\(O(n)\)的时间去更新,自然是不优的 可以发现这个更新可以用数据结构来维护,对于每个hash值开 ...

  6. Bzoj 1040 [ZJOI2008]骑士 题解

    1040: [ZJOI2008]骑士 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5368  Solved: 2044[Submit][Status ...

  7. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  8. [POI2008]KLO && POC

    题意:给定一个序列 s1, s2,...sn,以及一个k,求一个连续的k个数,把s[i]...s[i+k-1]变成一个数s',使得sigma(|s[j]-s'|)(i<=j<=i+k-1) ...

  9. 【POI 每日题解 #4】 [POI2008]MAF-Mafia

    [POI2008]MAF-Mafia 很容易看出是拓扑 但不容易想出来怎么做[可能是我太菜 首先 入度为零的人是肯定死不了的 接着 我们分成环和链分析 对于一个链 最多的情况就是顺着一个个开枪 最后剩 ...

  10. 【BZOJ1125】【POI2008】poc - splay+哈希

    题意: Description n列火车,每条有l节车厢.每节车厢有一种颜色(用小写字母表示).有m次车厢交换操作.求:对于每列火车,在交换车厢的某个时刻,与其颜色完全相同的火车最多有多少. Inpu ...

随机推荐

  1. MyBatis 的在使用上的注意事项及其辨析

    1. MyBatis 的在使用上的注意事项及其辨析 @ 目录 1. MyBatis 的在使用上的注意事项及其辨析 2. 准备工作 3. #{ } 与 ${ } 的区别和使用 {} 3.1 什么情况下必 ...

  2. AI赋能ITSM:企业运维跃迁之路

    随着企业信息化建设的深入,IT运维管理作为保证企业信息系统稳定运行的重要工作,越来越受到重视. 那么,什么是IT运维呢? 简单地说,IT运维是一系列维护.管理和优化企业IT基础设施.系统和应用程序的活 ...

  3. 微信和支付宝异步回调通知IP白名单

    附一:微信支付回调通知出口IP列表 商户侧对商户回调通知功能开通白名单网段: 上海电信出口网段:101.226.103.0/25 上海联通出口网段:140.207.54.0/25 上海CAP出口网段: ...

  4. 基于Web3.0的区块链图片上传

    开始前,我们先简单了解一下基本的概念,我大致归纳为以下几个点 什么是Web3.0,和区块链又有什么关系?(上回的文章不就派上用场了) 需求:开发一个基于Python的Web 3.0图片上传系统.这个系 ...

  5. Stable Diffusion 生成个性图片指南

    在当今人工智能领域,midjourney无疑是生成图片的王者,但是苦于付费才能使用,今天我就给大家分享一下midjourney平替stable diffusion,实现本地生成不逊色于midjourn ...

  6. VSCode中设置用IPython运行Python代码

    VSCode中设置用IPython运行Python代码 在IPython中运行所选的代码: 在设置中, 找到python.terminal.launchArgs这一项, 设置为如下内容. " ...

  7. QT学习:04 代码化的界面绘制

    --- title: framework-cpp-qt-04-代码化的界面绘制 EntryName: framework-cpp-qt-04-ui-design-by-code date: 2020- ...

  8. Centos 下Rabbit MQ 常用操作指令 汇总

    1.rabbitmq 服务的启动停止. service rabbitmq-server start service rabbitmq-server stop 重启命令需要依次执行 stop 再 sta ...

  9. 对linux的理解--个人理解

    linux系统中的命令我觉得可以和windows上的点点点,如文件的查找,文件的新建.删除,用户的添加.删除等来对比理解.一个是点点点,一个是用命令来完成. --------------------- ...

  10. Swift开发基础04-枚举

    基本用法 enum Direction { case north case south case east case west } enum Direction { case north, south ...