省选前模板复习

PREFACE

也许是OI生涯最后一场正式比赛了,说是省选前模板,其实都是非常基础的东西,穿插了英文介绍和部分代码实现

祝各位参加JXOI2019的都加油吧

也希望今年JX能翻身,在国赛中夺金

数学知识

数学知识小结

字符串

KMP算法Knuth-Morris-Pratt Algorithm

KMP算法,又称模式匹配算法,是用来在一个文本串(text string)s中找到所有模式串(pattern)w出现的位置.

它是通过当失配(mismatch)发生时,模式串本身能提供足够的信息来决定下一个匹配能从哪里开始,这样就可以避开对前面已经匹配好的字符的再次遍历检查(即暴力做法)

"In computer science, the Knuth–Morris–Pratt string-searching algorithm (or KMP algorithm) searches for occurrences of a "word" W within a main "text string" S by employing the observation that when a mismatch occurs, the word itself embodies sufficient information to determine where the next match could begin, thus bypassing re-examination of previously matched characters."

来源: 维基百科

我们需要构造一个函数next,next[n]表示的是模式串w中以第n个字符结尾的非前缀子串(substring)与w的前缀(prefix)能够匹配的最大长度

具体实现过程与思路见OI WIKI

简单说一下比较难懂的在w[i]!=w[j+1],为什么指针j跳向next[j]

显然w[1~j]是等于i-1j个字母的字串的,但是我们比较发现w[j+1]!=w[i]

我们想要的新的j是使得除了第\(j+1\)个字符需要与w[i]比较外,w[1]~w[j]的前缀等于i-1(包括i-1)前j个字母的后缀(suffix)

又由于i-1前任意k(k<=next[j])个字符后缀等于next[j]前任意k个字母的后缀

又因为w[1~next[next[j]]]等于next[j]next[next[j]]长度的后缀,即等于i-1next[next[j]]长度的后缀

所以新的j=next[j]就满足上述条件

n=strlen(a+1);m=strlen(b+1);//a is the text string
ne[1]=0;//next
for(ri i=2,j=0;i<=m;i++){
     while(j>0&&b[i]!=b[j+1])j=ne[j];
     if(b[i]==b[j+1])j++;
     ne[i]=j;
}
for(ri i=1,j=0;i<=n;i++){
     while(j>0&&a[i]!=b[j+1])j=ne[j];
     if(a[i]==b[j+1])j++;
     fail[i]=j;
     if(fail[i]==m){
     printf("%d\n",i-m+1);
     fail[i]=1;
     j=ne[j];
    }
}

哈希Hash Function

不想中文介绍了QAQ,OI中常用于数据的离散化(Discretization)

关于更多离散化内容

但是哈希函数有可能发生哈希冲突(Hash Collision),为了避免这种情况,我们可以采取双哈希或是拉链

"A hash function is any function that can be used to map data of arbitrary size onto data of a fixed size. The values returned by a hash function are called hash values, hash codes, digests, or simply hashes. Hash functions are often used in combination with a hash table, a common data structure used in computer software for rapid data lookup. Hash functions accelerate table or database lookup by detecting duplicated records in a large file. One such application is finding similar stretches in DNA sequences. They are also useful in cryptography."

来源:维基百科

字符串哈希我一般用Rabin-Karp Hash,更多相关内容在链接里

#define ull unsigned long long
ull x,hash_table[maxn];
char str[maxn];
void make_hash(int n){
    x=0;
    for(ri i=0;str[i];i++){
        x=x*131+a[i]-31;
        hash_table[i]=x;
    }
    return ;
}

对于两个串,我们可以将它们按上述方法映射(mapping)到哈希表后通过二分(binary serach)或是倍增(QAQ这个英语怎么说啊)来快速匹配

主要根据这个可以\(O(1)\)地判断两个字串是否相等

\(w_{str_{i,j}}\)

\(=(\) \(a_i\) \(p^{j-i}\)+\(a_{i+1}\) \(p^{j-i-1}\)+...+\(a_{j}\) \(p^0\))

\(=\) \(w_{pre_{j}}\) \(-\) \(w_{pre_{i-1}}\) \(p^{j-i+1}\)

数据结构Data Structure

栈Stack

栈是只有一端能进出元素的线性数据结构,所以它是后进后出(LIFO,Last In First Out)

入栈(push)出栈(pop)两种基本操作

"In computer science, a stack is an abstract data type that serves as a collection of elements, with two principal operations:

  • push, which adds an element to the collection, and
  • pop, which removes the most recently added element that was not yet removed.

The order in which elements come off a stack gives rise to its alternative name, LIFO (last in, first out). Additionally, a peek operation may give access to the top without modifying the stack.[1]The name "stack" for this type of structure comes from the analogy to a set of physical items stacked on top of each other, which makes it easy to take an item off the top of the stack, while getting to an item deeper in the stack may require taking off multiple other items first.[2]"

来源:维基百科

队列Queue

队列是一种"先进先出(FIFO,First In Fitst Out)"的线性数据结构.一般元素从右端入队,从左端出队.为了节省空间实现上可以采用循环队列

链表Linked List

(这几个都感觉没什么好说的)

讲一下邻接表(Adjacency List)吧,它可以看成是"带有索引数组的多个数据链表",这种所引就是链表的表头,表头又构成了一个表头(headers)数组

存图常用邻接表或是邻接矩阵(Adjacency Matrix)

邻接表

int num_edge=1;
struct Edge{
    int ne,to,dis;
}edge[maxm];
void add_edge(int f,int to,int dis){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    h[f]=num_edge;
    return;
}

字典树Trie

字典树是一种用于实现字符串快速检索的多叉树结构,其节点都拥有字符指针.

"Trie is an efficient information reTrieval data structure.Using trie,search complexities can be brought too optimal limit(O(M),M is key length).However the penalty is on Trie storage requirements."

int trie[maxn][27],tot=1;
bool end[maxn][27];
void Insert(){
    int t=tot,x;
    for(ri i=1;i<=len;i++){
        x=str[i]-'a';
        if(trie[t][x]==0)trie[t][x]=++tot;
        t=trie[t][x];
    }
    end[t][x]=1;
    return ;
}
bool Match_Ok(){
    int t=tot,x;
    for(ri i=1;i<=len;i++){
        x=str[i]-'a';
        t=trie[t][x];
        if(!t)return false;
    }
    return end[t][x];
}

二叉堆Binary Heap

二叉堆是一颗满足堆性质的完全二叉树.根据排序方式可以分为大根堆(Max-Heap)小根堆(Min-Heap)

对于大根堆,任意一个非根节点的权值都小于等于其父节点的权值;小根堆则反之

"A binary heap is a complete binary tree which satisfies the heap ordering property.The ordering can be one of two types:the min-heap property and the max-heap property.The value of each node of the max-heap is less than or equal to the value of its parent,with the maximum-value element at the root"

来源:cs.cmu.edu

int heap[maxn],n;//max-heap
void up_modify(int p){
    while(p>1){
        if(heap[p]>heap[p>>1]){
            swap(heap[p],heap[p>>1]);
            p=p>>1;
        }
        else break;
    }
    return ;
}
void insert(int val){
    heap[++n]=val;
    up(n);
}
void down(int p){
    int s=p<<1;
    while(s<=n){
        if(s<n&&heap[s]<heap[s+1])s++;//choost the maximum one of the two sons
        if(heap[s]>heap[p]){
            swap(heap[s],heap[p]);
            p=s,s=s<<1;
        }
        else break;
    }
    return;
}
void extract_top(){
    heap[1]=heap[n--];
    down(1);
    return ;
}

线段树Segment Tree

图论Graph Theory

Dijkstra算法Dijkstra Algorithm

DIjkstra算法是为了解决单源最短路径问题(Single Source Shortest Path,SSSP)的,简单来说,就是求原点到其它所有点(vertex pl.vertices)的最短路(shortest path)

它是基于一个贪心思想,要求所有边都是非负的.大致就是不断拓展一颗最短路生成树

"Dijkstra‘s algorithm is very similar to Prim‘s algorithm for minimum spanning tree(MST).Like Prim‘s MST,we generate a SPT(shortest path tree) with given source as root.We maintain two sets,one set contain vertices included in the shortest path tre,other set includes vertices not yet included in shortest path tree.At every step,we find a vertex which is in the other set and has a mimnum distance from the source."

来源:geeksforgeeks.org

#define ri register int
struct Vertex{
    int id,d;
    bool operator <(const Vertex & a)const{
        return d>a.d;
    }
    Vertex(){;}
    Vertex(int x,int y){id=x,d=y;}
};
priority_queue <Vertex> q;
void add_edge(int f,int to,int dis){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    h[f]=num_edge;
}
int dis[maxn];
bool vis[maxn];
void dijkstra(int s){
    int u,v,w;
    for(ri i=1;i<=n;i++){
        dis[i]=INF,vis[i]=0;
    }
    dis[s]=0;
    q.push(Vertex(s,0));
    while(q.size()){
        u=q.top().id,w=q.top().d;
        q.pop();
        if(vis[u])continue;
        for(ri i=h[u];i;i=edge[i].ne){
            v=edge[i].to;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                q.push(Vertex(v,dis[v]))
            }
        }
        vis[u]=1;
    }
    for(ri i=1;i<=n;i++)printf("%d\n",dis[i]);
    return ;
}

Bellman-Ford算法和SPFA算法Bellman-Ford Algorithm And SPFA

忽然发现ddl干不完了,因此介绍咕了

queue <int> q;
void spfa(){
    int u,v,w;
    for(int i=1;i<=n;i++)dis[i]=INF,vis[i]=0;
    dis[s]=0,vis[s]=1;
    q.push(s);
    while(q.size()){
        u=q.top();q.pop();
        vis[u]=0;
        for(int i=h[u];i;i=edge[i].ne){
            v=edge[i].to,w=edge[i].dis;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!dis[v]){q.push(v);dis[v]=1;}
            }
        }
    }
    for(int i=1;i<=n;i++)printf("%d\n",dis[i]);
    return ;
}

Floyd算法Floyd Warshall Algorithm

Floyd算法是基于动态规划思想来解决任意两点之间最短路径(All Pairs Shortest Path problem,APSP)的算法

亦可用于可传递的关系(传递闭包),这个也先挖坑吧

memset(d,0x3f,sizeof(d));
for(int i=1;i<=n;i++)dis[i][i]=0;
for(int i=1;i<=m;i++){
    scanf("%d %d %d",&x,&y,&z);
    dis[x][y]=min(dis[x][y],z);
}
for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        }
    }
}

Kruskal算法Kruskal Algorithm

Kruskal算法是求出一张给定联通边带权无向图的最小生成树算法.那么什么是最小生成树?

"Given a connected and undirected graph, a spanning tree of that graph is a subgraph that is a tree and connects all the vertices together. A single graph can have many different spanning trees. A minimum spanning tree (MST) or minimum weight spanning tree for a weighted, connected and undirected graph is a spanning tree with weight less than or equal to the weight of every other spanning tree. The weight of a spanning tree is the sum of weights given to each edge of the spanning tree."

来源:Geeksforgeeks 强烈安利这个网站,写的浅显易懂而且最重要的是,it is not blocked

Kruskal算法维护无向图的最小生成森林,每次在连接两个森林的边中找一条权值最小的将两个森林连起来.对于连通性我们使用并查集维护

struct Edge{
    int u,v,dis;
    bool operator <(const Edge & a)const {
        return dis<a.dis;
    }
}edge[maxm];
int fa[maxn];
int get(int x){
    if(fa[x]!=x)fa[x]=get(fa[x]);
    return fa[x];
}
void kruskal(){
    int u,v,w,cnt=0;
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].dis);
    }
    sort(edge+1,edge+1+m);
    for(int i=1;i<=m;i++){
        u=edge[i].u,v=edge[i].v;
        u=get(u),v=get(v);
        if(u!=v){
            fa[u]=v;
            cnt++;
        }
        if(cnt==n-1){
            break;
        }
    }
    return ;
}

树链剖分Tree Chain Partition

详细介绍咕了

将树上操作转化为线段树,由于重链数量级是\(log N\)的,线段树时间复杂度是\(log N\),总时间复杂度是\(log^2 N\)

还可以同时求LCA,但是操作时有许多细节需要注意,尤其是dfn[]rnk[]

int fa[maxn],dfn[maxn],rnk[maxn],w[maxn],dep[maxn],top[maxn],son[maxn],size[maxn];
int cnt=0;
void dfs_1(int now){
    int v;size[now]=1;
    for(int i=h[now];i;i=edge[i].to){
        v=edge[i].to;
        if(v==fa[now])continue;
        fa[v]=now,dep[v]=dep[now]+1;
        dfs_1(v);
        size[now]+=size[v];
        if(!son[now]||size[v]>size[son[now]])son[now]=v;
    }
    return ;
}
void dfs_2(int now,int t){
    int v;
    top[now]=t,dfn[now]=++cnt,rnk[cnt]=now;
    if(!son[now])return ;
    dfs_2(son[now],t);
    for(int i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==son[now]||v==fa[now])continue;
        dfs_2(v,v);
    }
    return ;
}
int L,R;
void operation_on_path(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        L=dfn[top[x]],R=dfn[x];
        operation_on_segmenttree(L,R);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y])swap(x,y);//x is the LCA
    L=dfn[x],R=dfn[y];
    operation_on_segmenttree(L,R);
    return;
}
void operation_on_subtree(int x){
    L=dfn[x],R=dfn[x]+size[x]-1;
    operation_on_segmenttree(L,R);
    return ;
}

动态规划Dynamic Programming

估计要咕了

原文地址:https://www.cnblogs.com/Rye-Catcher/p/10847144.html

时间: 2024-10-13 22:27:14

省选前模板复习的相关文章

[信息学]省选前模板整理

省选前把板子整理一遍,如果发现有脑抽写错的情况,欢迎各位神犇打脸 :) 数学知识 数论: //组合数 //C(n,m) 在n个数中选m个的方案数 ll C[N][N]; void get_C(int n) { for(int i=1;i<=n;i++) { C[i][i]=C[i][0]=1; for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } //欧几里得算法 //(a,b) ll gcd(ll a,ll b) { re

HDU 1031 Design T-Shirt 选前k大

相当于给出一组数列,然后选择前K大的数的算法. 本题没有给出详细的数据,故此就使用动态分配空间的方法了. 而这种题最好的算法就是使用快排思想,期望时间效率就是O(n)了. 最基本入门解决这种题的算法是直接排序了.那就成了水代码了.用上快排的思想才能体现出水平. 不过这种快排实在考的太多了,建议一定要掌握. 每次做这个算法的题目总会要调试一定时间的,每次都出现奇葩的错误.看来还是不够细心. 做题的时候一定要排除杂念,有干扰,后果很严重,会花长很多时间. 靖空间做题,一定要静,达到一种禅的境界.说禅

模板复习【updating】

马上就要noi了--可能滚粗已经稳了--但是还是要复习模板啊 LCT: bzoj2049 1A 7min # include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long dou

省选前的做题记录(Round2)

[CF1109B] Sasha and One More Name 先把无解情况aaabaaa.aaaaaa判掉,然后我们可以证明答案不会超过2. 对于一个回文串,两个指针从两端向内移动直到字符发生变化,在这里看一刀然后交换左右两端即为一种合法方案. 所以我们只需要判断答案是否能为\(1\),暴力判即可. [CF1109C] Sasha and a Patient Friend 假设我们有一个序列数据结构,那么先考虑每个结点需要维护哪些值才能支持合并左右儿子. 维护: 当前点时刻\(time\)

选前5 名的链表问题

  #include<iostream> #include<assert.h> #include<string> using namespace std; struct node { int x; node *next; node(int a){x=a;} }; class link { public : node *head; link(int *a) {   int n=5; head=new node(a[0]); node *p=head; while(--n)

写在省选前

以往我都会对每一场比赛进行很长时间的yy,把各种可能的结果都想了.后来结果出来既不会太糟又不会太好. 但是我很久没有享受过一场比赛了. gdkoi的时候我被虐的很惨,然后我很生气,然后我想在最后的时候争取一下.我还记得那天晚上,我一页一页翻看云的空间,整理了109道题,对自己说:写完它. 我写下那篇退役?再搏一次! 结果是没有写完,我觉得原因是我在最后的时候还是有点偷懒的. 但是我争取了.我觉得我没写完这个结果我可以接受,或者如果我写完它我可能会很高兴吧.但是更重要的时,从里面的题,我回顾了我这

强连通分量tarjan模板复习

对于一个有向图定点的子集,在该子集中任取两点u与v,都能找到一条从u到v的路径,则称该子集是强连通的.若该集合加入到任意点集中,它都不再强连通,则称这个子集是原图的一个强连通分量.任意一张图都可以分解成若干个不相交的强连通分量.这是强连通分量分解.把分解后的强连通分量缩成一个顶点,就可以得到一个有向无环图. 如图: 求一张图的强连通分量的个数,常用tarjan算法,它是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈

1亿个数选前100最大数

算法思想 1.选择一亿数的前100数前100个数排序 2.后面的数字通过插入排序 #include<iostream> using namespace std; void buddlesort(int array[],int n) { bool exchange=true; int count=n; while(exchange){ exchange=false; for(int i=0;i<count;i++) { if(array[i]>array[i+1]) { int te

C++函数模板复习

#include <iostream> #include <typeinfo> using namespace std; template <class T> T add(T one, T two) { cout << "类型:" << typeid(T).name() << endl; return one + two; // 函数模板只有在调用时才编译,有的编译器在初次编译时就会编译 } int add(int