一些比较基础的知识点
树链剖分
这是一个很简单的知识点.
主要思想是把树按照重链进行剖分.
重链指的是一条由顶点与重儿子相连,再由重儿子与重儿子的重儿子相连……组成的链。
而重儿子则指的是一个点所有儿子中size最大的节点.
有了这个工具,我们再求解许多问题时都可以方便许多.
例如,题目要求两点LCA,那么只需要这样打就好了:
int Go(int x, int y) {while (x ^ y) {if (top[x] == top[y]) return dep[x] > dep[y] ? y : x;if (dep[top[x]] < dep[top[y]]) swap(x, y);x = fa[top[x]];}return x;
}
唯一需要注意的就是跳重链时要选择跳完后深度更低的那个点跳。
预处理我们也可以很快的求出来,这样打:
void Dfs(I k) {E[++ E[0]] = k, sz[k] ++;for (I x = las[k], Hv = 0; x ; x = nex[x])if (!dep[tov[x]]) {fa[tov[x]] = k, dep[tov[x]] = dep[k] + 1;Dfs(tov[x]);Son[k] = sz[tov[x]] > Hv ? tov[x] : Son[k], sz[k] += sz[tov[x]], mx(Hv, sz[tov[x]]);}
}F(i, 1, E[0])top[E[i]] = Son[fa[E[i]]] == E[i] ? top[fa[E[i]]] : E[i];
- 套链剖的题太多了,这里就不讲了.
Huffman树
给出一颗包含nnn个节点的**kkk叉树**,其中第iii个叶子节点带权wiw_iwi,要求最小化∑wi∗li\sum w_i*l_i∑wi∗li,其中lil_ili表示第iii个叶子结点到根节点的距离。该问题的解被称为kkk叉
Huffman
树其实很早以前就已经接触过这个了,当时做过很经典的一道题:JZOJ4210
Problem
给定一个非严格递减序列AAA,Bi=∑j=inAjB_i=\sum_{j=i}^nA_jBi=∑j=inAj.
现在有一个n∗nn*nn∗n网络,左下角(1,1)(1,1)(1,1),右上角(n,n)(n,n)(n,n),要求从(n,1)→(1,1)(n,1)\rightarrow(1,1)(n,1)→(1,1).
走格方式:(x,y)→(x−1,y+1)(x,y)\rightarrow(x-1,y+1)(x,y)→(x−1,y+1),(x,⌈y+12⌉)(x,\lceil \frac{y+1}{2} \rceil)(x,⌈2y+1⌉),其中后者要支付BxB_xBx代价.
求最小代价。
Solution
可以感受到这道题的y+12\frac{y+1}{2}2y+1很类似二叉树上的某种操作,我们尝试着进行类比.
考虑一个非严格递减的序列AAA,它的
Huffman
树应该如何构造。我们令
f[i][j]
表示当前Huffman
树构造到序列的第iii个位置,即序列的前i−1i-1i−1个位置已经放好,现在准备放AiA_iAi到Huffman
树上,有jjj个叶子节点可以供放AiA_iAi这个数.注意前提AAA是降序的, 所以不难发现,每个点对应在
Huffman
树上的深度必定是升序的.所以不难写出转移:
f[i][j]+∑k=inak→f[i][2∗j]f[i][j] + \sum_{k=i}^n a_k \rightarrow f[i][2*j]f[i][j]+k=i∑nak→f[i][2∗j]f[i][j]→f[i+1][j−1]f[i][j] \rightarrow f[i +1][j - 1]f[i][j]→f[i+1][j−1]
含义分别是,把当前
Huffman
树上所有叶节点都扩展两个新叶节点,拿来供给以后i∼ni\sim ni∼n的节点放,但因为深度多了111,所以不管怎样,以后的节点都至少要多一个aka_kak贡献,所以相当于加上一个∑k=inak\sum_{k=i}^n a_k∑k=inak,另外一个转移则是直接把当前iii号节点放在jjj个叶节点的某一节点中.整个流程如下:
#include <cstdio>
#include <cstring>#define F(i, a, b) for (int i = a; i <= b; i ++)
#define G(i, a, b) for (int i = a; i >= b; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define mem(a, b) memset(a, b, sizeof a)const int N = 1e3 + 10;int n, m, T, Ans;
int a[N], S[N], f[N][N];int main() {scanf("%d", &n);F(i, 1, n) scanf("%d", &a[i]); S[n + 1] = 0;G(i, n, 1) S[i] = S[i + 1] + a[i];mem(f, 7), f[1][1] = 0;F(i, 1, n)F(j, 1, n)f[i + 1][j - 1] = min(f[i + 1][j - 1], f[i][j]),f[i][j * 2] = min(f[i][j * 2], f[i][j] + S[i]);Ans = 1e9;F(i, 1, n) Ans = min(Ans, f[n][i]);printf("%d\n", Ans);
}
此时再观察一下题目,不难发现,这TM不正是题目的逆问题吗?
考虑原问题的一个最优解,把其路径反过来,则正是DP的过程.
观察到题目是上取整,也正好符合构建
Huffman
树和DP的过程.所以原问题相当于对其
Huffman
树解的询问,这个可以在O(nlogn)O(nlogn)O(nlogn)时间内解决.代码如下:
#include <cstdio>
#include <set>#define F(i, a, b) for (int i = a; i <= b; i ++)using namespace std;const int N = 1e3 + 10;int n, T, x; long long Ans;
multiset <int> S;int main() {for (scanf("%d", &T) ; T --; ) {scanf("%d", &n), S.clear(), Ans = 0;F(i, 1, n) scanf("%d", &x), S.insert(x);F(i, 1, n - 1) {int x = *S.begin(); S.erase(S.begin());int y = *S.begin(); S.erase(S.begin());Ans += (long long)(x + y), S.insert(x + y);}printf("%lld\n", Ans);
}}
回到
Huffman
树的原问题中,上述讨论的实质上是二叉Huffman
树.如果讨论kkk叉树,方法也是类似的,唯一需要注意的是必须通过在序列里补000的方式把n−1n-1n−1变为k−1k-1k−1的倍数.
这是由于,我们必须让每次在
set
里选kkk个时,不能选满的一次,必须发生在Huffman
树的最底层.至此,有关哈夫曼树的问题暂且告一段落.
KMP
即模式匹配,能够在线性时间内求出字符串SSS在字符串TTT中所有出现过的位置.
其核心是nextnextnext数组
next[i]next[i]next[i] 表示 S[1∼next[i]]=S[n−next[i]+1∼n]S[1\sim next[i]] = S[n-next[i]+1\sim n]S[1∼next[i]]=S[n−next[i]+1∼n].
考虑普通的匹配,我们总是拿TTT串当中的某一位置与SSS串某一位置进行比较,不妨设当前枚举到TTT串的第iii个位置,匹配了SSS串的前jjj个位置,现在要判断的是T[i]T[i]T[i]与S[j+1]S[j+1]S[j+1]是否相等.
如果相等,则直接jjj指针加一即可,否则我们可以令j=next[j]j=next[j]j=next[j],这里就完美体现了KMP的巧妙之处.
即不浪费每一次的比较.
next[1] = 0;
for (int i = 2, j = 0; i <= n; i ++) {while (j > 0 && a[i] != a[j + 1]) j = next[j];if (a[i] == a[j + 1]) j ++;next[i] = j;
}for (int i = 1, j = 0; i <= m; i ++) {while (j > 0 && (j == n || b[i] != a[j + 1])) j = next[j];//这里需要注意,我们要找出所有S在T中出现位置,如果已经j==n了,则必须把j==next[j]if (b[i] == a[j + 1]) j ++;if (j == n)S在T中出现了一次.
}
Hash
Hash
在NOIP范围内运用的很多,如【NOIP2014day2】解方程一题,运用普通Hash
可以拿到很不错的分数.虽然正解更加巧妙,但同样是运用了
Hash
的思想。一般来说,
Hash
可以看做是一个表,每个数xxx以H(x)=(xmodP)+1H(x)=(x\ mod\ P)+1H(x)=(x mod P)+1形式得到的值存进去,其中PPP一般是一个很大的质数,因为不能保证没有冲突,所以一般Hash
都会与链表同在,以保证时间复杂度.但是在许多
Hash
种,如字符串Hash
,我们一般是拿Hash
值来进行比较,而并非存到Hash
表中.所以很常见的一种方法是按进制处理,然后自然溢出(用
unsigned long long
类型储存即可),把字符串看成PPP进制,其中PPP取131或13331131 或 13331131或13331的时候效果很好.来看一道例题:jzoj5462
我们直接运用上述的方法,代码如下:
#include <cstdio>#define F(i, a, b) for (int i = a; i <= b; i ++)const int N = 2e5 + 10,beed1 = 13331, beed2 = 131, k1 = 1231231, k2 = 1123513,m1 = 123456, m2 = 654321;int n, m, Ans; bool bz1[m1 + 10], bz2[m2 + 10];
unsigned long long F1[N], F2[N], B1, B2, x, y;
char ch[N];int main() {freopen("article.in", "r", stdin);freopen("article.out", "w", stdout);scanf("%d %d %s", &n, &m, ch + 1);B1 = B2 = 1;F(i, 1, m)B1 *= beed1, B2 *= beed2;F(i, 1, n) {F1[i] = F1[i - 1] * beed1 + (ch[i] - 'a') + k1;F2[i] = F2[i - 1] * beed2 + (ch[i] - 'a') + k2;}F(i, m, n) {x = F1[i] - F1[i - m] * B1; x = x % m1;y = F2[i] - F2[i - m] * B2; y = y % m2;if (!bz1[x] && !bz2[y])Ans ++;bz1[x] = bz2[y] = 1;}printf("%d\n", Ans);
}
运用了自然溢出取模,但实际得分令人震惊:
然后我尝试把模数开大一点,这时候分数依然令人震惊:
- 然后我再尝试着开5个自然溢出的数组,这时候我才发现,原来代码的这一个地方打错了,&&应该改为||.
if (!bz1[x] && !bz2[y])Ans ++; //这是错误的
bz1[x] = bz2[y] = 1;if (!bz1[x] || !bz2[y])Ans ++; //这才正确
bz1[x] = bz2[y] = 1;
但我发现,即使改正过后,并且多开很多个数组进行判断,拿的分依旧不超过70分。
证明,自然取模虽然简单好用,但在特殊构造的数据下,极其容易出错.
70分的代码如下:
#include <cstdio>#define F(i, a, b) for (int i = a; i <= b; i ++)const unsigned long long N = 2e5 + 10,beed1 = 13331, beed2 = 131, beed3 = 123123153, beed4 = 998244353,k1 = 6522331231, k2 = 5549260917, k3 = 23121451532, k4 = 915398244353,m1 = 5123456, m2 = 6543321, m3 = 1231231, m4 = 7812434;int n, m, Ans; bool bz1[m1 + 10], bz2[m2 + 10], bz3[m3 + 10], bz4[m4 + 10];
unsigned long long F1[N], F2[N], F3[N], F4[N], B1, B2, B3, B4, x, y, X, Y;
char ch[N];int main() {freopen("article.in", "r", stdin);freopen("article.out", "w", stdout);scanf("%d %d %s", &n, &m, ch + 1);B1 = B2 = B3 = B4 = 1;F(i, 1, m)B1 *= beed1, B2 *= beed2, B3 *= beed3, B4 *= beed4;F(i, 1, n) {F1[i] = F1[i - 1] * beed1 + (ch[i] - 'a') + k1;F2[i] = F2[i - 1] * beed2 + (ch[i] - 'a') + k2;F3[i] = F3[i - 1] * beed3 + (ch[i] - 'a') + k3;F4[i] = F4[i - 1] * beed4 + (ch[i] - 'a') + k4;}F(i, m, n) {x = F1[i] - F1[i - m] * B1; x = x % m1;y = F2[i] - F2[i - m] * B2; y = y % m2;X = F3[i] - F3[i - m] * B3; X = X % m3;Y = F4[i] - F4[i - m] * B4; Y = Y % m4;if (!bz1[x] || !bz2[y] || !bz3[X] || !bz4[Y])Ans ++;bz1[x] = bz2[y] = bz3[X] = bz4[Y] = 1;}printf("%d\n", Ans);
}
其中后三个数据,与答案的差距非常大,观察数据后发现,其只由两个字母构成,且有循环节,并且针对了自然溢出出数据.
我们尝试着用普通的取模进行操作,然后随便取几个模数,就可以过了:
#include <cstdio>#define F(i, a, b) for (int i = a; i <= b; i ++)const long longN = 2e5 + 10,beed1 = 13331, beed2 = 131, beed3 = 123123153, beed4 = 998244353, beed5 = 123456789,k1 = 6522331231, k2 = 5549260917, k3 = 23121451532, k4 = 915398244353, k5 = 13515315347,m1 = 9123456, m2 = 8543321, m3 = 8231231, m4 = 7812434, m5 = 9154782;int n, m, Ans; bool bz1[m1 + 10], bz2[m2 + 10], bz3[m3 + 10], bz4[m4 + 10], bz5[m5 + 10], bz;
long long F1[N], F2[N], F3[N], F4[N], F5[N], B1, B2, B3, B4, B5, x, y, X, Y, A;
char ch[N], S[N];int main() {freopen("article.in", "r", stdin);freopen("article.out", "w", stdout);scanf("%d %d %s", &n, &m, ch + 1);scanf("%s", S + 1);B1 = B2 = B3 = B4 = B5 = 1;F(i, 1, m)B1 = (B1 * beed1) % m1,B2 = (B2 * beed2) % m2,B3 = (B3 * beed3) % m3,B4 = (B4 * beed4) % m4,B5 = (B5 * beed5) % m5;F(i, 1, n) {F1[i] = (F1[i - 1] * beed1 + (ch[i] - 'a') * k1) % m1;F2[i] = (F2[i - 1] * beed2 + (ch[i] - 'a') * k2) % m2;F3[i] = (F3[i - 1] * beed3 + (ch[i] - 'a') * (ch[i] - 'a') * k3) % m3;F4[i] = (F4[i - 1] * beed4 + (ch[i] - 'a') * (ch[i] - 'a') * (ch[i] - 'a') * k4) % m4;F5[i] = (F5[i - 1] * beed5 + (ch[i] - 'a') * k5) % m5;}F(i, m, n) {x = ((F1[i] - F1[i - m] * B1) % m1 + m1) % m1;y = ((F2[i] - F2[i - m] * B2) % m2 + m2) % m2;X = ((F3[i] - F3[i - m] * B3) % m3 + m3) % m3;Y = ((F4[i] - F4[i - m] * B4) % m4 + m4) % m4;A = ((F5[i] - F5[i - m] * B5) % m5 + m5) % m5;if (!bz1[x] || !bz2[y] || !bz3[X] || !bz4[Y] || !bz5[A])Ans ++;bz1[x] = bz2[y] = bz3[X] = bz4[Y] = bz5[A] = 1;}printf("%d\n", Ans);
}
综上所述,带取模的hash是很难被卡的,然而直接自然溢出是极容易被卡的.
其中,对于如何卡自然溢出,以及不用双hash或者多hash的卡法,在这片题解中写的很详尽
https://jzoj.net/senior/index.php/main/download/5462/article.pdf/0/solution_path
强联通分量
我对这个知识点的定义是一个比较简单,但却很容易打错的算法.
这个算法没有必要赘述(主要是赘述不清)
求有向图当中强联通分里个数代码:
#include <cstdio>#define F(i, a, b) for (int i = a; i <= b; i ++)
#define mn(a, b) ((a) = (a) < (b) ? (a) : (b))const int N = 2e5 + 10;using namespace std;int n, x, y, cnt, num, top; bool ins[N];
int dfn[N], low[N], tov[N], nex[N], las[N], stack[N], tot;void link(int x, int y) { tov[++ tot] = y, nex[tot] = las[x], las[x] = tot; }void Tarjan(int k) {dfn[k] = low[k] = ++ num;stack[++ top] = k, ins[k] = 1; //注意这个地方与接下来的点双边双有区别,要保证一个点在栈里面才进行下面的mn(low[k], dfn[tov[x]])操作.for (int x = las[k] ; x ; x = nex[x])if (!dfn[tov[x]]) {Tarjan(tov[x]);mn(low[k], low[tov[x]]);}else if (ins[tov[x]]) mn(low[k], dfn[tov[x]]);if (dfn[k] == low[k]) {++ cnt;while (k ^ (y = stack[top --]))ins[y] = 0;}
}int main() {scanf("%d", &n);F(i, 1, n)scanf("%d", &x), link(i, x);F(i, 1, n)if (!dfn[i]) Tarjan(i);printf("%d\n", cnt);
}
一些比较基础的知识点相关推荐
- js中当等于最小值是让代码不执行_JavaScript中最最基础的知识点
JavaScript 中有很多很常用的也很基础的知识点需要我们牢牢记住,倒背如流,这样在开发的时候才能得心应手. 本文主要总结了DOM,Array,String,Math的一些常用方法,还有一些JS编 ...
- 这些Java基础面试知识点,你都掌握了吗?
想了解一下学Java的小伙伴们,在学习Java的时候有没有去总结知识点,哪些知识点我们没有掌握,哪些知识点是我们还没有掌握的,今天小编来发掘一些Java基础面试知识点,不知道大家掌握没有. 小编分享的 ...
- 全国计算机二级公共知识点,全国计算机二级公共基础知识知识点
全国计算机二级公共基础知识知识点 公共基础知识 第一章 数据结构与算法 1.1 算法 1.1.1 算法的基本概念 1.算法的基本特征 可行性.确定性.有穷性.拥有足够的情报 所谓算法,是一组严谨地定义 ...
- 河南招教考试计算机专业知识,河南教师招聘考试《计算机网络技术基础》知识点归纳七...
河南教师招聘考试<计算机网络技术基础>知识点归纳七 1.ADSL是非对称数字用户线路,其下行速率为1.5-8 Mb/s,而上行速率则为16-640 kb/s.在一对铜双绞线上的传送距离可达 ...
- CSS基础必备知识点01
CSS基础必备知识点 CSS(Cascading Style Sheme), 层叠样式表或级联样式表,简称样式表.它的作用是给HTML网页设置外观或者样式.其中外观或者样式指的是:HTML网页中的文字 ...
- 计算机二级考试c语言公共知识,2016年电大最新计算机二级考试c语言公共基础题知识点.doc...
2016年电大最新计算机二级考试c语言公共基础题知识点 计算机二级考试c语言公共基础题知识点第一章 数据结构与算法 1.1 算法 算法:是指解题方案的准确而完整的描述. 算法不等于程序,也不等计算机方 ...
- 计算机音频和视频知识点,计算机基础的知识点.docx
计算机基础的知识点 第二章.计算机原理:冯.诺伊曼提出的 储存程序控制 原理进行工作的CPU 包括 1)寄存器组 2)运算器 数据来自寄存器,结果也回寄存器保存 进行算术运算和逻辑运算 3)控制器 C ...
- 【C++】-- C++11基础常用知识点(下)
上篇: [C++]-- C++11基础常用知识点(上)_川入的博客-CSDN博客 目录 新的类功能 默认成员函数 可变参数模板 可变参数 可变参数模板 empalce lambda表达式 C++98中 ...
- 网络安全基础课程知识点(1)
网络安全基础课程知识点(1) 一.OIS七层模型图 1.应用层 通过人机交互来实现各种服务,用户接口,应用程序(文件传输,电子邮件,文件服务,虚拟终端). 所需设备:网关. 由抽象语言→ 编码 2.表 ...
- javaSE基础重点知识点总结 持续更新
javaSE基础重点知识点解析 文章目录 javaSE基础重点知识点解析 前言 1.重载和重写 2.匿名对象 3.可变个数形参的方法 4.MVC设计模式 5.java.lang.Object类 6.自 ...
最新文章
- R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化、并添加y=0的水平横线)、uniroot函数求解方程的根(并添加方程根对应的垂直竖线)
- CH341SER CH340SER USB转串口驱动
- 如何才能写好一篇文章?
- mysql 一对一关联查询_学习ThinkPHP的第20天--MySQL事务操作、查询事件、一对一关联...
- Java中的生成器设计模式
- dev c++调试怎么看变量的值_利用GDB调试 MSQL
- MyEclipse 2017 CI 9 发布(附下载)
- 掌握鸿蒙轻内核静态内存的使用,从源码分析开始
- Cuda:invalid device pointer
- 与孩子一起学编程04章
- 简单论坛的搭建(Discuz)、memchche、OpenResty(类nginx)
- Linux系统常用测试工具
- 一个微信账号只能开发一个微信小程序吗?
- Python 自然语言处理笔记(五)——信息检索系统,基于Lucene实现
- about-page
- python代码螺旋线怎么写_用Python绘制三轴对数螺旋线
- 基于JAVA EE的临床科室管理系统
- jenkins安装Publish Over SSH
- 一次新公司注册与小程序上线的历程
- 微信返利机器人开发制作