树链剖分

  • 这是一个很简单的知识点.

  • 主要思想是把树按照重链进行剖分.

  • 重链指的是一条由顶点与重儿子相连,再由重儿子与重儿子的重儿子相连……组成的链。

  • 重儿子则指的是一个点所有儿子中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=in​Aj​.

  • 现在有一个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∑n​ak​→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=in​ak​,另外一个转移则是直接把当前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);
}

一些比较基础的知识点相关推荐

  1. js中当等于最小值是让代码不执行_JavaScript中最最基础的知识点

    JavaScript 中有很多很常用的也很基础的知识点需要我们牢牢记住,倒背如流,这样在开发的时候才能得心应手. 本文主要总结了DOM,Array,String,Math的一些常用方法,还有一些JS编 ...

  2. 这些Java基础面试知识点,你都掌握了吗?

    想了解一下学Java的小伙伴们,在学习Java的时候有没有去总结知识点,哪些知识点我们没有掌握,哪些知识点是我们还没有掌握的,今天小编来发掘一些Java基础面试知识点,不知道大家掌握没有. 小编分享的 ...

  3. 全国计算机二级公共知识点,全国计算机二级公共基础知识知识点

    全国计算机二级公共基础知识知识点 公共基础知识 第一章 数据结构与算法 1.1 算法 1.1.1 算法的基本概念 1.算法的基本特征 可行性.确定性.有穷性.拥有足够的情报 所谓算法,是一组严谨地定义 ...

  4. 河南招教考试计算机专业知识,河南教师招聘考试《计算机网络技术基础》知识点归纳七...

    河南教师招聘考试<计算机网络技术基础>知识点归纳七 1.ADSL是非对称数字用户线路,其下行速率为1.5-8 Mb/s,而上行速率则为16-640 kb/s.在一对铜双绞线上的传送距离可达 ...

  5. CSS基础必备知识点01

    CSS基础必备知识点 CSS(Cascading Style Sheme), 层叠样式表或级联样式表,简称样式表.它的作用是给HTML网页设置外观或者样式.其中外观或者样式指的是:HTML网页中的文字 ...

  6. 计算机二级考试c语言公共知识,2016年电大最新计算机二级考试c语言公共基础题知识点.doc...

    2016年电大最新计算机二级考试c语言公共基础题知识点 计算机二级考试c语言公共基础题知识点第一章 数据结构与算法 1.1 算法 算法:是指解题方案的准确而完整的描述. 算法不等于程序,也不等计算机方 ...

  7. 计算机音频和视频知识点,计算机基础的知识点.docx

    计算机基础的知识点 第二章.计算机原理:冯.诺伊曼提出的 储存程序控制 原理进行工作的CPU 包括 1)寄存器组 2)运算器 数据来自寄存器,结果也回寄存器保存 进行算术运算和逻辑运算 3)控制器 C ...

  8. 【C++】-- C++11基础常用知识点(下)

    上篇: [C++]-- C++11基础常用知识点(上)_川入的博客-CSDN博客 目录 新的类功能 默认成员函数 可变参数模板 可变参数 可变参数模板 empalce lambda表达式 C++98中 ...

  9. 网络安全基础课程知识点(1)

    网络安全基础课程知识点(1) 一.OIS七层模型图 1.应用层 通过人机交互来实现各种服务,用户接口,应用程序(文件传输,电子邮件,文件服务,虚拟终端). 所需设备:网关. 由抽象语言→ 编码 2.表 ...

  10. javaSE基础重点知识点总结 持续更新

    javaSE基础重点知识点解析 文章目录 javaSE基础重点知识点解析 前言 1.重载和重写 2.匿名对象 3.可变个数形参的方法 4.MVC设计模式 5.java.lang.Object类 6.自 ...

最新文章

  1. R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化、并添加y=0的水平横线)、uniroot函数求解方程的根(并添加方程根对应的垂直竖线)
  2. CH341SER CH340SER USB转串口驱动
  3. 如何才能写好一篇文章?
  4. mysql 一对一关联查询_学习ThinkPHP的第20天--MySQL事务操作、查询事件、一对一关联...
  5. Java中的生成器设计模式
  6. dev c++调试怎么看变量的值_利用GDB调试 MSQL
  7. MyEclipse 2017 CI 9 发布(附下载)
  8. 掌握鸿蒙轻内核静态内存的使用,从源码分析开始
  9. Cuda:invalid device pointer
  10. 与孩子一起学编程04章
  11. 简单论坛的搭建(Discuz)、memchche、OpenResty(类nginx)
  12. Linux系统常用测试工具
  13. 一个微信账号只能开发一个微信小程序吗?
  14. Python 自然语言处理笔记(五)——信息检索系统,基于Lucene实现
  15. about-page
  16. python代码螺旋线怎么写_用Python绘制三轴对数螺旋线
  17. 基于JAVA EE的临床科室管理系统
  18. jenkins安装Publish Over SSH
  19. 一次新公司注册与小程序上线的历程
  20. 微信返利机器人开发制作

热门文章

  1. 视频去除原声添加新的音乐时如何控制音量大小
  2. 手游聊天软件遇到大型攻击怎么办?游戏盾SDK清洗无忧
  3. PHP手机浏览器全屏,手机浏览器设置全屏
  4. Java、JSP网上银行系统的设计与实现
  5. Java毕业设计 基于SSM的毕业设计选题推荐 SSM婚纱摄影网站 婚纱摄影管理系统 婚纱摄影小程序
  6. ory Oathkeeper docker-compose 安装运行
  7. 理解Subjects, Principals and Credentials
  8. CreateTextFileamp;OpenTextFile
  9. AIMERSION的共识机制
  10. Xftp下载及基本使用