济南学习杂记

胡乱记了点,方便之后复习

8.6没记

8.7有点少

今天讲的数论和数据结构,上午讲的内容,上课的老师认为“友好”;
我。。
其实听的还能够,毕竟以前学过,exgcd和线性筛和exgcd求逆元;node

Exgcd()
是求解ax+by = gcd(a,b)的算法
Ax + by = m当且仅当m%gcd(a,b) == 0时有解
由于gcd(a,b) == gcd(b,a %b)
因此只要咱们找到bx2+(a%b)y2==gcd(b,a%b)
就能够
A%b ==a – a/bb
因此bx2+(a%b)y2 ==ay2+b(x - a / b
y)
当b==0时x=1是方程解,而后回溯算法

欧拉筛
for(int i=2; i<=n; i++){ 
    if(!vis[i]) prime[cnt++]=i; 
    for(int j = 0; j < cnt && i * prime[j] <= n; j++){ 
        vis[i * prime[j]]=prime[j]; 
        if(i % prime[j] == 0) break; //防止一个数不是被他的最小因子筛掉
//例如若是没有这句,12会被i==4,j==2时筛掉,但实际上应该被i==6,j==1时筛掉
    } 
}

欧拉筛不只能够筛素数还能够求最小质因子数组

逆元
若是AB % M = 1
那么A、B互为在模M下的逆元
若是A、M不互质,那么A不存在模M下的逆元。
A
B%M == AB+MY
--------------------------------------------网络

Int mod_inverse(int a, int m){
Int x, y;
Exgcd(a, m, x, y);
Return (x % m + m) % m;
}

可是后面的题目都是英文不说(老师翻译了),还没什么思路,数据结构

下午讲的数据结构多是老师预计的没讲完,讲的二叉堆,二叉搜索树,线段树,树状数组优化

和几道题目,相比数论仍是好不少的翻译

下午的题目至少还听懂了好几道,可是只实现出了一个,并且没有oj评测和数据点,而后下午大部分时间在写一道上古时代就应该会的题指针

迪杰斯特拉堆优化code

而后一直没写出来,最后理解了可是仍是不过,我吧我看的那篇题解代码沾上去也不过。。。。。我一脸懵,我写了这么长时间怎么不对。。。blog

8.8

用什
二维树状数组

•void add(int x,int y, int val)
•{
•     for(int i=x;i<=n;i+=lowbit(i))
•         for(int j=y;j<=m;j+=lowbit(j))
•             s[i][j]+=val;
•}

都是加一维(以添加为例)

并查集
路径压缩

Find(int x){return x == fa[x] ? x : f[x] = find(f[x])}
按秩合并
    对于每一个并查集维护一个size数组,每次合并两个数组时将小的合并到大的上
X = find(x);y = find(y);
If(size[x] > size[y]) {fa[y] = x;size[x] += size[y]};//初始化时令每个size=1;
        Else{fa[x] = y;size[y] += size[x]};

只写路径压缩复杂度就很低,很难被卡掉,不过卡常的时候能够用按秩合并

关押罪犯
(作过,讲过不知道多少遍的题......)
1.二分答案将<当前答案的边连起来,判断是否为二分图(黑白染色法)
//二分图断定

vector<int> e[N];
int col[N]; //col[u]表示u的颜色,1为白色,-1为黑色,0为未染色
bool flag=true; //是否为二分图
void dfs(int u,int c) {
    col[u]=c;
    for(int i=0;i<e[u].size();++i) {
        int v=e[u][i];
        if(!col[v]) dfs(v,-c);
        else if(col[v]==c) flag=false;
    }
}
for(int i=1;i<=n;++i)
    if(!col[i]) dfs(i,1);

by老师

2.从大到小排序边(两人的怒气值),每次若是x和没有敌人,就将x的敌人设成y,不然就将x和y的敌人放到同一个并查集,表明在同一个监狱,y和x操做相同
当咱们发现当前枚举的边关系的两我的x,y已经在同一集合,咱们虽然有可能经过其余方式使他们不在同一集合,但那样会使其余更大的边连起来(由于咱们从大到小枚举,枚举当前边以前其余边已经要求并查集就是这样,因此咱们只能让这件事发生,他们的影响力即为答案)

星球大战
因为并查集不支持删边操做,可是并无强制在线
咱们能够倒着加边,即先求最后删完边,再加一条边(倒数第二个删的边)
求倒数第二个状况,以此类推

例8
一个无相连通图,每条边都有权值,两个点互相到达的代价是他们通过路径的最大权值
每次询问
输出全部互相到达代价小于k的两个城市
离线处理,按k值从小到大排序,是一个加边的过程,用并查集维护连通块

Trie字典树
用二维数组存ch[n][m]n表明最长串的长度,m表明每个串的每个字符有多少不一样的状况
例如:每个都是小写字母组成的字符串m就是26

插入:
初始化
O = 0;

查看字符串中第i个字符有没有在o的后面现过(即ch[o][第i点]是否不等于0)
,出现过就让”指针”o = 当前节点的标号
没有就新建一个点ch[o][当前点]=++cnt,o = cnt
`i++,重复以上操做

例9
给定n个数求出其中两个数异或最大值
将全部的数补至30位(2进制)(为了从最高位比较),而后放到踹树里(二进制)
每次选取一个数,在踹树上匹配,由于咱们从最高位匹配,因此若是如今咱们能够选择0或1

(1)当前选择1的话假设之后都是最坏状况(即每次都只能选0)异或后答案是1后面全是0
(2)和当前选0的话之后都是最好状况(即每次都能选1)大啊是0后面全是1

因此咱们看出当前选1的话不管如何都会优于选0,因此每次贪心选,(固然,若是没有1只能选0),把n个数都[]跑完以后去max

例十
•维护一个数据结构,支持:
•1.插入一个正整数x(x<=10^9)
•2.询问已插入的数中有多少个与y异或获得的结果>=z(y,z<=10^9)
•操做数<=10^5
查询的时候,好比说咱们要求结果大于10011,咱们当前若是走1这条路最差是1000000(比10011大),这棵树就不须要递归了,直接加上这颗树的大小(须要记一个size数组)
当前选0的话最好状况是01111(小于10011),咱们也不须要递归,由于永远得不到咱们想要的结果,直接忽略,这样比暴力忧不少

例11

这个改题面为查询的时候p没有限制
维护每一个点的前缀和,放到踹树中,全部数的异或和为sum,每次找与x^sum异或后的最大值,
由于a^a = 0,因此将sum和某一个前缀异或获得的就是当前位置开始的后缀

St表
倍增的思想,解决的是,RMQ问题(即某一区间的最大或最小值)
F[i][j]表明从i开始的2的j次方个节点的最值
F[i][j] = max(f[i][j – 1],f[f[i][j - 1]+ 1][j - 1])
即第i个节点开始到第2的j次方后的节点的最值=max(i开始第2的j-1次方的最值,i开始第2的j – 1次方开始再2的j – 1次方的最值)
即,一个区间最值 = max(他左一半的最值,右一半的最值)
好比1到4的区间最值(f[1][2]) = 1到2最值(f[1][1])和3到4最值(f[3][1])中大的那个

LCA的三种解决方式
1倍增:
F[i][j]表明第i个节点向上第2的j次方后的节点
F[i][j] = f[f[i][j – 1]][j - 1]
即第i个节点向上第2的j次方后的节点=(第i个节点向上跳2的j – 1次方后的节点)再向上跳2的j – 1 次方的节点
由于2的j – 1次方加2的j – 1次方 = 2的j次方

2转换RMQ问题
某个巨佬已经证实出RMQ和LCA能够互相转换.
(1)求出树的dfs序
(2)两个点u,v的LCA是pos[u]到pos[v]中深度最小的点
Ps:pos[x]表明x在dfs中个出现的顺序,u表明较早出现的点,v和u相反

3用tarjan算法
然而我并不会tarjan...

•①记录每一个点的询问
•②dfs回溯时用并查集合并(将子结点集合并到父节点集合,以父节点为根)
•③查询lca(u,v)时假设此时u已访问,v刚访问到,那么u的并查集的根即为答案
强行复制,逃.

Hash
哈希容易出现哈希碰撞,可使用无错哈希
开m个vector(m为哈希的模数),每次遇到相同模数的时候将当前值放进去,每一个点的指望仍是O(1),因此对空间和时间影响不大

双模哈希理论能够被卡,可是很是困难,因此能够用,若是被卡了就是脸黑木办法hhh
即每个数模两个不一样的数,只有两个模数都相同,才能证实出现过

哈希还能够用来记录状态,好比八数码问题使得判重只须要O(1)

8.9今天听得也不咋地

图论
克鲁斯卡尔算法证实

N==1时,最小生成树显然
在n == k成立时,n == k + 1有一条边(u,v)是链接新节点的最短边,由于须要构成一颗树,因此,这个点必定要连出一条边,假设不是最短边,咱们就将最短边连起来,在一棵树中连一条不重复的边会构成环,那么环中任意删除一条边仍是树,因此,连上最短边再删除一条环中边确定更优

例1
N个村庄,每一个村庄要么打一口井,要么从某一个有水的地方建一个管道,问最小代价
有水☞
(1)村庄建了井
(2)村庄向有水的地方建了一个管道
建另外一个节点0为”水井”,他和每个节点连边,边权为节点建水井的费用
这样只要求最小生成树便可

货车运输
方法一
克鲁斯卡尔算法
用克鲁斯卡尔算法求最大生成树,
用倍增求LCA,顺便求出路径最小值
方法二
Kruskal重构树
在kruskal的过程当中
若当前边所连两点u和v不在一个集合内
则新建一个节点node,点权为该边边权
而后链接u所在集合的根与node以及v所在集合的根与node
重构完成以后,指定每一个集合的根做为所在森林的根
则u到v路径上最大边权的最小值 就是LCA(u,v)的点权
强行复制,逃.

例3温馨的路线
给定一张图,n个点,m条边,求一棵生成树,知足它的边权最小值最大。输出这个最小值

直接用克鲁斯卡尔求最大生成树
另外一个方法 玄学 && 找”出阿宝”作法,二分答案

Spfa算法判负环,
我无论了.....弄了半天也没搞懂原理,反正就是每一个点进队n - 1次就说明有负环
也有dfs的作法,每次枚举每个点,每次走负边(1到2是-2,2到3是1也算负边),若是一个点再次被搜到,就说明有负环

Tarjan算法
Dfn[n]表明点n第几回搜到
low[n]表明n及n搜索树子树中的点中能访问到的点dfn的最小值
本身写个伪代码

塔尖(int x){
标记为x访问过
For遍历每一条边(递归每一个子树)
若是当前节点没有访问过
递归子树根节点
Low[x]取x和子树根最小值
不然
Low[x]和子树根的low取min
}
若是dfs[n] == low[n]
记录强连通份量个数的变量++
将栈中所有元素弹出,标记vis[元素] = n表明这些元素在这个强连通份量里

间谍网络
咱们已知的某些受贿的间谍,和他们须要的钱。咱们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每一个间谍分别用1到3000的整数来标识。
请根据这份资料,判断咱们是否有可能控制所有的间谍,若是能够,求出咱们须要支付的最少资金。不然,输出不能被控制的一个间谍。

首先tarjan缩点(如下是有解的状况),
对于没有环的图,咱们选择入度为零的人进行贿赂,其他有入度的人都会被揭发
对于有环的图,咱们塔尖缩点,就变成了有向无环图,这个点的代价就是须要最少钱被贿赂的那我的须要的钱

差分约束

差分约束系统由形如xi - xj <= k这样的式子组合而成,
求xs - xt最大值
求解方法:
(1)将形式变成xi - xj <= k
(2)令xj向xi连一条长度为k的边
(3)求出s到t的最大值

图中有负环则解不存在,没法互相到达则答案为无限大(由于没有什么限制他)

今天其实听得不怎么好,有不少东西没有记下来

8.11

动态规划
状态与记忆化搜索
状态其实就是问题的子问题
记忆化搜索 vs 动态规划

无需考虑顺序 须要考虑顺序
容易实现,容易理解 不容易实现,不容易理解
常数大 常数小
优化困难 部分容易优化

线性动态规划
小齐挖矿
如今有m + 1个星球,从左到右标号为0到m,小奇最初在0号星球。
有n处矿体,第i处矿体有ai单位原矿,在第bi个星球
每次只能调到x + 4或x + 7的位置,问最多挖多少矿

经过%#$@^%@!能够发现,两个星球只要相隔距离大于17,就必定能到达
因此咱们把全部距离相隔大于17的星球距离设为18,这样就只须要18 * n的空间
以及只须要18 * n的时间

大搬家
如今有一个长度为n的搬家指示,其中ai表示住在第i栋房子的人须要搬家到第ai栋房子
N家连续搬家,结果,第3次搬家结果跟第一次结果相同
问有多少种不一样的数列答案对1000000007取模

最大子矩阵
有一个n*2的矩阵,请你选出其中k个子矩阵,使得这
个k个子矩阵分值之和最大。子矩阵不能互相重叠

状态太多直接复制ppt上的吧
定义状态
fi,j表示前i行,选了j个子矩阵,当前两列不属于任何矩阵的最大值。
gi,j表示前i行,选了j个子矩阵,当前两列属于同一个矩阵的最大值。
hi,j表示前i行,选了j个子矩阵,当前两列属于不一样矩阵的最大值。
si,j表示前i行,选了j个子矩阵,左边一列属于一个矩阵的最大值。
ti,j表示前i行,选了j个子矩阵,右边一列属于一个矩阵的最大值.

玩具取名
名字是从”WING”四个字母中选出一个,而后通过一系列变化变成名字

区间dp,设f[i][j]][k]为i 到j这个区间k能不能被合成
每次枚举区间时,枚举每个变化方案,看看可否由小的区间合并成这个区间
染色
长度为n的木板每次课选择一个连续的区间染色,后染的会覆盖先染的
问最少染几回
F[i][j] 表示i到j的区间染色的最小值
若是a[i] == a[i + 1] 那么f[i][j] = f[i + 1][j]
若是a[j - 1] == a[j] 那么f[i][j] = f[i][j - 1]
若是a[i] == a[j] 那么 f[i][j] = f[i + 1][j],f[i][j - 1],f[i][j]
除了这些之外,f[i][j] = f[i][mid]+f[mid + 1][j]
以上式子都要取min

而后大多数题都没记笔记,实际由于都不会,晚上剩下的时间作了一道区间dp,加深理解吧(这道题难度远远低于今天讲的题目的难度)

相关文章
相关标签/搜索