暑假清北学堂集训笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假清北学堂集训笔记相关的知识,希望对你有一定的参考价值。

day -1

    订票订得晚只好坐凌晨1点的火车……

day 0

    7点钟到北京了,坐滴滴到酒店,然后去华北电力大学报道,路上看到一辆共享单车,弄了大半天才发现是坏的。。。

    报完到就在旁边的餐厅吃了起来。

day 1

南小鸟(钟皓曦)讲 搜索 分治 倍增 贪心

ST表: f[i][j]表示 从i到i+2^j-1这2^j个位置的元素最大值 初始化: f[i][0]=z[i] 转移: f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])

LCA(最近公公祖先):f[i][j] 表示从树上编号为i的点向上走2^j步会走到哪个点 初始化: f[i][0]=father[i] 转移: f[i][j]=f[f[i][j-1][j-1]

求a,b,LCA时将a置为深度深的那个点,然后一直往上走,知道两个点深度相同,再将两个点同时向上走,走到同一个点时,那个点就是他们的LCA

inline void dfs(int now,int f) {
    for (unsigned i = 0;i < edges[now].size();i++)
		if (!deep[edges[now][i]] && edges[now][i] != f) {
	                deep[edges[now][i]] = deep[now]+1;
			father[edges[now][i]][0] = now;
			dfs(edges[now][i],now);
	        }
}
void init() {  
    for (int j = 1;(1<<j) <= n;j++)
        for (int i = 1;i <= n;i++)
            if (father[i][j-1] != -1) father[i][j] = father[father[i][j-1]][j-1];
}  
inline int lca(int a,int b) {
    if (deep[a] < deep[b]) swap(a,b);
    int i;
    for (i = 0;(1<<i) <= deep[a];i++);
    i--;
    for (int j = i;j >= 0;j--)
        if (deep[a]-(1<<j) >= deep[b]) a = father[a][j];
    if (a == b) return a;
    for (int j = i;j >= 0;j--) {
        if (father[a][j] != -1 && father[a][j] != father[b][j]) {
            a = father[a][j];
            b = father[b][j];
        }
    }
    return father[a][0];
}    

快速幂:快速幂是倍增和分治的结合,如:2^50可以分为(2^25)^2  2^25又可以分为(2^12)^2*2一直这样,我们就可以在O(log n)时间内求出x^n

inline int QuickPow(int x,int y) {
    if (y == 1) return x;
    int z = QuickPow(x,y>>1);
    if (y&1) return z*z*x%mod;
    return z*z%mod;
}

二分查找:二分查找可以在一个单调的序列中用O(log n)的时间找出一个数,它是一种分治

inline int find(int l,int r,int x) {
    if (r == l) return l;
    int mid = l+r>>1;
    if (x < a[mid]) return find(l,mid);
    return find(mid,r);
}

贪心:贪心一般形式是,以某种方式将所有物品排序,排序后按照从小到大进行选择

搜索:dfs,bfs

搜索优化:

    剪枝:把不优于当前最优解的状态剪掉

    卡时:卡时是一种骗分技巧,一般用于最优性问题,当程序准备超时时,直接输出当前答案,然后结束程序

#include <cstdlib>
#include <cstdio>
#include <ctime>
int t,ans;
inline void dfs(...) {
    if (clock-t >= 990) {
        printf("%d",ans);
        exit(0);
    }
    ...
}
int main() {
    t = clock();
    ...
    dfs(...);
    printf("%d",ans);
    return 0;
}

晚上测试

第一题是一题不可做的模拟题!!!

我写里1.5h+

结果还是没能调出来

第二题写了一个四重循环加卡时

第三题没看

结果

……

0+0+0=0

爆零啦!!!!!!!!!!!!!  有大佬200 Orz %%%

day 2

一上午都在讲数论,作为五年级小学生表示听不懂!!!!!!!!!!!!!!!!!!!

中午南小鸟走了,杨乐来了

杨乐讲了一些背包和记忆化搜索

day 3

上午,杨乐又讲了一些线性dp

LIS:最长不下降子序列

    假设我们需要求以x结尾的最长下降子序列dp[x],由最忧性可得,我们除去最后一个位置(也就是x),还是一段最长下降,

    那我们可以枚举这个子序列的结尾y,最优值就是dp[y]。但需要注意的是,必须保证A[x] < A[Y], x比 Y要低,才满足下降的要求。

    我们从所有枚举的结果中找到一个最大的即可。

    转移方程:dp[i] = max{dp[j]+1} (j<i     a[i]<a[j])

LCS:最长公共子序列

    假设我们需要求两个序列分别以i,j结尾的最长公共子序列

    dp[i][j], 接下来我们可以分几种情况讨论:

    a[i]不在公共子序列中,那么长度则等于dp[i-1][j]

    b[j]不在公共子序列中,那么长度则等于dp[i][j-1]

    a[i]与b[j]都在子序列中,并且两者匹配,那么长度等于dp[i-1][j-1]+1

然后还讲了区间dp,二维平面dp,状态划分dp

下午杨乐又讲了序列划分dp,树形dp,状态压缩dp……

day 4

杨乐也走了,黄致焕来了

黄致焕讲了栈,单调栈,队列,单调队列

day4就这样愉快的结束了

day 5

黄致焕又讲了并查集,堆,可并堆,树状数组,左偏树等离奇数据结构。。。

并查集:

    并查集是一种树形数据结构支持合并,查找祖先

    查找如下:

inline int find(int x) {  //father[x]表示x的祖先,father[x]=x表示x是根节点
    if (father[x] == x) return x;  //找到根节点
    return father[x] = find(father[x]);  //祖先的祖先就是我的祖先
}

    合并如下:

inline void Union(int x,int y) { father[find(x)] = find(y); }  //合并祖先

堆:

    定义:一棵满足以下两个性质的二叉树:

    1.父节点权值大于等于(大根堆)或小于等于(小根堆)任何一个儿子的权值。

    2.每个节点的左子树和右子树都是一个堆。

左偏树:

    左偏树是可并堆的一种

    在左偏树中,每个节点包含两个属性:权值和距离(dist)。除了之前二叉堆支持的操作以外,左偏树还支持合并(merge)操作。 

    左偏树的每个节点都满足做儿子的dist大于右儿子的dist,每个节点的dist的值即为该子树中与该节点最近的点的距离。

    合并如下:

inline int merge(int x,int y) {
    if (!x || !y) return x|y;  //判断空树
    if (heap[x].key < heap[y].key) swap(x,y);  //让键值大的为x
    heap[x].rson = merge(y,heap[x].rson);  //将右儿子与y合并
    if (heap[heap[x].lson].dist < heap[heap[x].rson].dist) swap(heap[x].lson,heap[x].rson);  //让dist大的为左儿子
    heap[x].dist = heap[heap[x].rson].dist+1;  //算dist
    return x;
}

    删除如下:

inline int del(int x) { return merge(heap[x].lson,heap[x].rson); }  //将左右儿子合并

然后老师又讲了很迷很迷的一些数据结构……

day 6

姜志豪来了,图论也来啦

树的遍历

    BFS:一开始队列中有一个点,将一个点出队,将它的子结点全都入队。 

    DFS:递归到一个点时,依次递归它的子结点。

最小生成树

    prim:

        先随机找一个点x作为根,然后维护一个集合S(存已经连通的点)和一个集合D(存当前连接S中所有点的线段,两端点都在S中的除外) 

        初始化S={x},D={x所连接的所有边}; 

        每次在D中选一个权值最小的边,然后将该边删去。该边所连接的另一个点放入S中,直到S中点数=全部点数。 

        这里记顶点数n,边数m,

        时间复杂度:O(m log n)

    kruskal:

        将边权从小到大排序,每次选择边权最小的,如果当前边连接的点已经连通了,就不选这条边。 

        利用并查集维护是否连通。 

        m为图中的边数 

        时间复杂度:O(m log m)

最短路算法

    floyd:

        设dist[i][j]为i~j的距离,则有动态转移方程:dist[i][j] = max{dist[i][k]+dist[k][j]}

    dijkstra:

        a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则正常有权值,若u不是v的出边邻接点,则权值为∞。 

        b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。 

        c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。 

        d.重复步骤b和c直到所有顶点都包含在S中。 

    spfa:

        首先建立一个数组s,s[i]代表从始点到i点所需要的最短距离。 

        利用一个队列进行松弛操作: 

        初始化s[i]=∞; 

        首先将始点入队,然后将始点所连接的点x入队。入队的时候,将始点到x的距离d赋值给s[i]。对于始点所连接的所有点都进行如下操作,弹出队首元素。 

        访问队首元素y,将队首元素y所连接的所有的点都入队。假如其中一个点为z,则判定一下s[z]是否大于s[y]+< y,z >边权。如果大于,则松弛一下,把s[y]赋值为较小的那个。

toposort:

    入度:有多少条边指向他 

    出度:有多少条边从他发出 

    先统计每个点的入度,假如a->b,则in[b]++; 

    用栈来维护入度=0的点。 

    若栈非空,则在栈中弹出一个元素(并输出),然后枚举这个点能到的每一个点将它的入度-1.如果入度=0,则压入栈中。 

    如果没有输出所有的顶点,则有向图中一定存在环 

强连通分量:

    ……

晚上,第二波考试

第一题:水题,n^2大暴力

第二题:不会做,仍然n^2大暴力

第三题:想到正解,写炸了!!!

100+20+30=150  有大佬270 Orz %%%

day 7

    Trie树,KMP,AC自动机……

day 111

    离noip还有1周……

以上是关于暑假清北学堂集训笔记的主要内容,如果未能解决你的问题,请参考以下文章

2017清北学堂集训笔记——动态规划Part2

网课总结——2022清北学堂

集训总结

清北学堂国庆day2解题报告

暑假集训D11总结

清北学堂Day1