浅谈哈希及一类应用杂题

news/2024/10/23 9:10:12/文章来源:https://www.cnblogs.com/spaceswalker/p/18494375

浅谈哈希及一类应用杂题

关于哈希的一些另类想法

PS:与后文实际应用无关

哈希的目的本质就是比较两个无法直接比较是否相同的一些东西,通过赋值来使其获得比较大小的能力,然后就想能不能搞一个随手就能整出来还不容易被卡常数比如之前好多题卡 \(131\) 什么的。如果我的数是纯随机的即使不是质数也不一定会出问题,就写了个这么个玩意儿试了一下。

namespace Rand_hash
{
typedef unsigned int ull;int vv[20000007];ull get_hash(int col){if (vv[col]) return vv[col];vv[col] = col * rand() * rand();return vv[col];}
}using namespace Rand_hash;

然后好像能用。。。。。。

这个坑先留着,以后有别的实验机会再说。

P7605 [THUPC2021] 小 E 爱消除

\(f(l,r)\) 为答案

转移 \(f(l,r)\) 考虑第一步选择了左边还是右边,不妨设左边。

如果最终都没消去,那么就是 \(f(l + 1,r) + (1, 1)\)

如果最终利用 \(c_k\) 消去了,考虑一下 \(c_k\) 是从左端取出的还是
从右端取出的。

不妨设是右端取出的,那么显然 \([k + 1,r]\) 要先被删除,考虑
枚举 \(j\) 表示 \([l + 1, j]\) 辅助了这一段的删除,那么转化后的子
区间就是 \([j + 1, k − 1]\)

\(\max\{f(j + 1, k − 1),2, 1 + g(l + 1, j, k + 1,r)\}\) 来表示这个转移

其中 \(g(l_1,r_1, l_2,r_2)\) 表示完全消除 \([l_1,r_1] ∪ [l_2,r_2]\) 所需要的栈的大小。当然也有可能为无穷,此时对应的状态不能用来转移

关于 \(g\) 的转移,考虑是先取出 \(l_1\) 还是 \(r_2\)

比如是 \(l_1\),考虑它和哪个位置配对,比如是和 \(c_i\),其中 \(i ∈ [l_2,r_2]\)。再枚举左侧辅助消除 \([i + 1,r_2]\) 的区间是 \([l_1 + 1, j]\),那么转移就可以表示为 \(\max\{g(l_1 + 1, j, i + 1, r_2) + 1, g(j + 1,r_1, l_2, i-1)\}\)

理论复杂度 \(O(n^6)\) ,实际上可以快速判断一些 \(g\) 的值为无穷,比如区间长度和是否为偶数,区间内的数是否两两配对,可以哈希然后异或和,但是如果使用前文瞎搞的那个随机化也可以并且不用脑子。可以用记忆化搜索实现中间的计算。

#include <bits/stdc++.h>#define rint register int
#define int long long
#define endl '\n'using namespace std;//哈希 
namespace Rand_hash
{
typedef unsigned int ull;
#define inc(p) make_pair(p.first + 1, p.second + 1)
#define Max(l, r) make_pair(max(l.first, r.first), max(l.second, r.second))int vv[20000007];ull get_hash(int col){if (vv[col]) return vv[col];vv[col] = col * rand() * rand();return vv[col];}
}using namespace Rand_hash;const pair<int, int> inf = make_pair(0x3f3f3f3f, 0x3f3f3f3f);
const int N = 5e1 + 5;
const int Inf = 0x3f3f3f3f;
int n, w[N], s[N][N][N][N];
ull x[N];
pair<int, int> f[N][N];//记忆化搜索计算 g 数组 
int g(int a, int b, int c, int d) 
{if (a > b && c > d) return 0;if (x[b] ^ x[a - 1] ^ x[d] ^ x[c - 1] || ((b - a + d - c) & 1)) return Inf;if (~s[a][b][c][d]) return s[a][b][c][d];int &res = s[a][b][c][d];res = 0x3f3f3f3f;if (a <= b) {for (rint i = a + 1; i <= b; i++) if (w[a] == w[i]) for (rint j = c; j <= d + 1; j++) res = min(res, max(g(a + 1, i - 1, j, d) + 1, g(i + 1, b, c, j - 1)));for (rint i = c; i <= d; i++) if (w[a] == w[i]) for (rint j = a; j <= b; j++) res = min(res, max(g(a + 1, j, i + 1, d) + 1, g(j + 1, b, c, i - 1)));}if (c <= d) {for (rint i = a; i <= b; i++) if (w[d] == w[i]) for (rint j = c - 1; j <= d - 1; j++) res = min(res, max(g(a, i - 1, j + 1, d - 1) + 1, g(i + 1, b, c, j)));for (rint i = c; i < d; i++) if (w[d] == w[i]) for (rint j = a - 1; j <= b; j++) res = min(res, max(g(a, j, i + 1, d - 1) + 1, g(j + 1, b, c, i - 1)));}res = max(2ll, res);return res;
}pair<int, int> sol(int a, int b, int c, int d)
{int res = g(a, b, c, d);if (res > n) return inf;return make_pair(0ll, max(2ll, res + 1ll));
}signed main() 
{cin >> n;for (rint i = 1; i <= n; i++) cin >> w[i], x[i] = x[i - 1] ^ get_hash(w[i]);for (rint i = 1; i <= n; i++) for (rint j = i; j <= n; j++)f[i][j] = inf;memset(s, -1, sizeof s);for (rint r = 1; r <= n; r++) {for (rint l = r; l != 0; l--) {f[l][r] = inc(min(f[l + 1][r], f[l][r - 1]));for (rint k = l + 1; k <= r; k++) {if (w[l] == w[k]) {for (rint j = k + 1; j <= r + 1; j++) f[l][r] = min(f[l][r], Max(f[k + 1][j - 1], sol(l + 1, k - 1, j, r)));for (rint j = l; j < k; j++) f[l][r] = min(f[l][r], Max(f[j + 1][k - 1], sol(l + 1, j, k + 1, r)));}				}for (rint k = l; k < r; k++) {if (w[r] == w[k]) {for (rint j = k + 1; j <= r; j++) f[l][r] = min(f[l][r], Max(f[k + 1][j - 1], sol(l, k - 1, j, r - 1)));for (rint j = l - 1; j < k; j++) f[l][r] = min(f[l][r], Max(f[j + 1][k - 1], sol(l, j, k + 1, r - 1)));}				}}}cout << f[1][n].first << " " << f[1][n].second << endl;return 0;
}

[NOI2024 D1T1] 集合

NOI 的出题方向果然和 APIO WC 对应,提高思维量但是算法本身的难度在逐渐下降。比较两个集合序列之间是否等价,很自然的想到哈希,但是直接哈希的复杂度是不能接受的,把哈希值扔进 set 里再去维护显然扯淡,但是可以拿到相当客观的暴力分数,如果选择使用线段树维护哈希值应该是可以做的,用 zkw 线段数然后快读应该能卡进去,但是双指针更优,对于任意左右边界 \(l\)\(r\),如果该边界合法并且存在 \(l + 1\)\(l + 1\) 也合法。对于 \(∀i\),找对应最右位置 \(j\) 最后复杂度应该是线性的。

UVA1502

引入 2025 HE 队爷 ReTF 的题解。

\(f_i\) 表示以 \(i\) 开头的最长子序列

\[f_i=\max\limits_{j\in(i,n]\land \,s_i\text{ ∈}s_j} f_j+w_i \]

\(w=\sum |s_i|\),则不同的串的长度不会超过 \(\sqrt{w}\) 种。

用哈希表存每个串 \(s_i\) 及其 \(dp\)\(f_i\),对于每一个串枚举在他之前所有出现过的串的长度 \(l\),对于每一个 \(l\) 找出该串所有长度为 \(l\) 的子串,在哈希表里查询即可实现转移。复杂度只有一个根号。

[NOI2022] 挑战 NPC Ⅱ

判断是否同构,显然考虑哈希。如果两棵树对应儿子子树同构,根节点儿子数量相等且存在两棵树的根儿子的唯一相互对应关系,那么通过这个性质可以把数据规模从一棵树缩小到子树规模,考虑 dp。对于哈希函数 \(f[i]\),其值只与子节点的个数和大小有关系。所以计算树哈希函数时随便加点乘一乘就行。判同构子树匹配时直接暴力可以二分图,但是复杂度会很高,观察注意到若 \(a∈son_x\) 同构于 \(b∈son_y\) 的子树直接匹配掉就行。所以不用二分图。至于剩下的也好处理,由于 \(k\) 很小记搜就好,中间减点枝就好。复杂度大概是 \(O(nK)\) 的,\(K\) 的大小不太清楚是指数级还是阶乘级,但是一定不会 T,因为远远跑不到这个上界,而且这个 \(K\) 是关于 \(k\) 的,不会很大。

int c, t, k;
int pw[N];
unordered_map<int, int> f[N];namespace Rand_hash
{
const int Typ = 11451419;
typedef unsigned int ull;int vv[20000007];ull get_hash(int col){if (vv[col]) return vv[col];vv[col] = (col * rand() % Typ * rand()) % Typ;return vv[col];}
}using namespace Rand_hash;struct node 
{vector<int> e[N];int n, r, f[N], sz[N];void dfs(int x) {f[x] = get_hash((int)e[x].size());sz[x] = 1;for (rint y : e[x]) {dfs(y);f[x] = f[x] * f[y] % mod;sz[x] += sz[y];}f[x] = (f[x] + Typ) % mod;sort(e[x].begin(), e[x].end(), [&](int x, int y) {return f[x] < f[y];});}void _clear() {cin >> n;for (rint i = 1; i <= n; i++) e[i].clear();for (rint i = 1; i <= n; i++) {int x;cin >> x;if (x != -1) e[x].push_back(i);else r = i;}dfs(r);}
} g, h;bool dfs(int x, int y) 
{if (g.e[x].size() < h.e[y].size()) return 0;if (g.sz[x] == h.sz[y]) return g.f[x] == h.f[y];if (f[x].find(y) != f[x].end()) return f[x][y];vector<int> s1, s2;int idx1 = 0, idx2 = 0;while (idx1 < g.e[x].size() && idx2 < h.e[y].size()) {if (g.f[g.e[x][idx1]] == h.f[h.e[y][idx2]]) {idx1++;idx2++;}else if (g.f[g.e[x][idx1]] < h.f[h.e[y][idx2]]) s1.push_back(g.e[x][idx1++]);else  s2.push_back(h.e[y][idx2++]);}while (idx1 < g.e[x].size()) s1.push_back(g.e[x][idx1++]);while (idx2 < h.e[y].size()) s2.push_back(h.e[y][idx2++]);if (s1.size() > g.sz[x] - h.sz[y]) return 0;vector<int> d(s1.size());for (rint i = 0; i < s1.size(); i++) d[i] = i;do {bool flag = 1;for (rint i = 0; i < s2.size(); i++) {flag &= g.sz[s1[d[i]]] > h.sz[s2[i]];}if (!flag) continue;for (rint i = 0; i < s2.size(); i++) {flag &= dfs(s1[d[i]], s2[i]);}if (flag) {f[x][y] = 1;return f[x][y];}} while (next_permutation(d.begin(), d.end()));f[x][y] = 0;return f[x][y];
}
signed main() 
{cin >> c >> t >> k;while (t--) {g._clear(), h._clear();for (rint i = 1; i <= g.n; i++) f[i].clear();puts(dfs(g.r, h.r) ? "Yes" : "No");	}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/819500.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PbootCMS授权码可以更换域名吗? 授权码丢失怎么办?

授权码可以更换域名吗?不可以:授权码是绑定特定域名的,如果需要更换域名,建议重新获取新的授权码。授权码丢失怎么办?重新获取:如果授权码丢失,可以重新访问授权页面,输入相同的域名再次获取授权码。扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉…

PbootCMS授权码是否可以用于不同域名的子域名?是否可以用于不同域名的子目录?

授权码是否可以用于不同域名的子域名?不可以:授权码是绑定特定域名的,不支持不同域名的子域名。例如,sub1.example.com 和 sub2.anotherdomain.com 需要分别获取授权码。18. 授权码是否可以用于不同域名的子目录?不可以:授权码是绑定特定域名的,不支持不同域名的子目录。…

PbootCMS验证码不显示或显示不清楚怎么办

验证码不显示或显示不清楚问题描述:后台登录时验证码不显示或显示不清楚。 解决方案:避免使用中文路径:确保所有文件和目录名称均为英文或数字。 切换PHP版本:推荐使用PHP 7.3、7.2、5.6版本。 检查文件权限:确保验证码相关文件和目录具有适当的读写权限(通常为755或644)…

值得信赖的FTP替代方案有哪些,一文带你详细了解!

FTP(文件传输协议)因其传输速度慢、安全隐患、管理复杂性、稳定性不足以及审计难题等缺陷,使得企业在寻找更高效的替代方案时显得尤为迫切。 FTP替代方案有哪些,简单了解看下吧: 1、SFTP:SFTP是建立在SSH(Secure Shell)协议之上的文件传输协议,提供了数据传输的加密和…

HTTP 2.0 新特性

HTTP 2.0 新特性HTTP 2.0 为什么使用二进制分帧?二进制协议比文本协议更加紧凑,减少占用空间 分帧层相当于将 HTTP 切分,更加灵活,比如可以对 header 帧做单独的特殊处理 分帧层有着属于自己的报文头,其中的 Stream Identity 使得操作系统具备将多个响应以及请求一一匹配的…

Python脚本检测笑脸漏洞

Python脚本检测笑脸漏洞 一、漏洞介绍 ​ vsftpd2.3.4中在6200端口存在一个shell,使得任何人都可以进行连接,并且VSFTPD v2.3.4 服务,是以 root 权限运行的,最终我们提到的权限也是root;当连接带有vsftpd 2.3.4版本的服务器的21端口时,输入用户中带有“😃 ”,密码…

Veritas Backup Exec 24.0 发布,新增功能概览

Veritas Backup Exec 24.0 发布,新增功能概览Veritas Backup Exec 24.0 发布,新增功能概览 Veritas Backup Exec 24.0 (Windows) - 面向中小型企业的数据备份和恢复 请访问原文链接:https://sysin.org/blog/veritas-backup-exec-24/ 查看最新版。原创作品,转载请保留出处。…

slope trick

slope trickP4597 序列 sequence 首先考虑 \(dp\) 。 由于只需将序列改为非严格递增,那么就有一个贪心,即最终答案的数集不会变大。 为什么呢? 这是因为只有序列某一位置严格递减时,才会进行修改。 修改可以将前面的数降到和后面的数一样大,或者将后面的数提到和前面的数一…

黄绿题选刷

[ABC376D] Cycle找到包含节点 1 的环,直接从节点一出发,BFS,如果第二次遍历到了节点1,直接输出时间即可。点击查看代码 #include <bits/stdc++.h> using namespace std; #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) #define ROF(i, a, b) for (int i …

面试题:如何能够保证T2在T1执行完后执行,T3在T2执行完后执行?——CountDownLatch原理

CountDownLatch的使用方式 CountDownLatch用于某个线程等待其他线程执行完任务再执行,与thread.join()功能类似。常见的应用场景是开启多个线程同时执行某个任务,等到所有任务执行完再执行特定操作,如汇总统计结果。 面试题:如何能够保证T2在T1执行完后执行,T3在T2执行完后…

读数据工程之道:设计和构建健壮的数据系统17存储的原材料

存储的原材料1. 存储 1.1. 存储是数据工程生命周期的基石1.1.1. 是数据获取、转换和服务主要阶段的基础1.1.1.1. 当构建数据管道时,随着数据经过获取、转换和服务阶段,工程师会选择适当的抽象来存储他们的数据1.1.2. 当数据在生命周期中移动时,它会被多次存储1.1.2.1. 必须在…

JAVA 前三次题目集总结

在过去的一个月里完成了java的前三次大作业对于JAVA的语法以及面向对象编程还不台上手,接下来说前三次大作业。 前三次大作业要是围绕答题判题系统展开的每次作业都在完善这个程序的功能可以说 1.第一次作业判分功能 在第一次作业阶段,核心任务是建立一个能够接收题目信息和答…