正在加载

请稍等

第一次加载可能稍慢

提高组模板大全

Author: ljc Time: 2025/10/29 Tag: csps 复赛 笔记

提高组算法模板大全

代码中大部分使用的 int 类型,需要按照事实修改成 long long

基本

万能头文件版本:

#include<bits/stdc++.h>
using namespace std;

int main(){

}

不含万能头版本

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<array>
#include<random>
#include<unordered_map>
#include<unordered_set>
using namespace std;

int main(){

}

加速读入:

ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

快读快写:

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x * f;
}
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

常用基本代码:

#include<bits/stdc++.h>
using namespace std;
// Main Definition
int main(){
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    // Main Code
}

基础算法

前缀和/差分

for(int i=1;i<=n;i++){
    sum[i] = sum[i-1]+a[i];
} // Find(l,r): sum[r]-sum[l-1];
for(int i=1;i<=n;i++){
    dif[i] += dif[i-1];
} // Change(x,to): dif[x]=to;

二分

int l=-INF,r=INF;
while(l<r){
    int mid = (l+r>>1);
    if(check(mid)){
        l = mid+1;
    }else{
        r = mid;
    }
}

STL 速查

常用函数

set

用于维护一个有唯一元素的集合。

insert(元素): 插入一个元素。

erase(元素): 删除一个元素。

find(元素): 查找一个元素。(如果等于.end()则没有此元素)

size(): 返回容器中元素的数量。

empty(): 检查容器是否为空。

multiset

与set相似,维护一个可以相同的元素集合。

queue

先进先出。

empty(): 检查队列是否为空。

size(): 返回队列中的元素数量。

front(): 返回队首元素的引用。

back(): 返回队尾元素的引用。

push(): 在队尾添加一个元素。

pop(): 移除队首元素。

deque

双端队列。

front():返回第一个元素。

back():返回最后一个元素。

begin():返回第一个元素的迭代器。

end():返回最后一个元素的迭代器。

empty(): 检查队列是否为空。

size(): 返回队列中的元素数量。

clear():清空。

insert(iterator pos,value) :在pos位置插入value元素。

push_back(value):在队尾添加value。

push_front(value):在队头添加value。

pop_back():删除队尾元素.

pop_front():删除队头元素

priority_queue

优先队列。

priority_queue<int> pq;
priority_queue<int,vector<int> > pq; // 大根堆
priority_queue<int,vector<int>,greater<int>> pq; // 小根堆

empty(): 检查队列是否为空。

size(): 返回队列中的元素数量。

top(): 返回队列顶部的元素(不删除它)。

push(): 向队列添加一个元素。

pop(): 移除队列顶部的元素。

map

字典,用于存储键值对。

map<key_type, value_type> mp;

mp[key]=value:赋值

mp[key]:查看元素

empty(): 检查是否为空。

size(): 返回元素数量。

clear():清空map。

multimap

与map相似,可存储相同键值对。

bitset

优化bool数组使用。

bitset<N> b;

b[i]获取i位bool值。

count():返回 1 的个数。

size():返回位数。

test(pos):检查某一位是否为 1

all():检查是否所有位都为 1

any():检查是否有任何一位为 1

none():检查是否所有位都为 0

&:按位与

|:按位或

^:按位异或

~:按位取反

图论

概念

顶点:图上的每一个点。

无向图:边没有方向。

有向图:边有方向。

边:顶点与顶点之间的连线。

权:边的数值。

无向完全图:在无向图中,边数达到$n(n-1)/2$的图。

有向完全图:在有向图中,边数达到$n(n-1)$的图。

稀疏图:在$n$个顶点,$e$条边的图中,满足$e<n\log_2n$的图。

稠密图:在$n$个顶点,$e$条边的图中,满足$e\geq n\log_2n$的图。

子图:假设$G=(V,{E}),G_2=(V_2,{E_2})$,如果$V_2\subseteq V \text{且} E_2\subseteq E$,则$G_2$为$G$的子图。

领接点:两个顶点之间有一条边则为领接点。

出度、入度。

连通图:无向图中,任意两个顶点之间有路径,则为连通图。

连通分量:无向图中,极大连通子图称为连通分量。

强连通图:有向图中,任意两点连通。

强连通分量:有向图中,极大连通子图。

生成树:包含全部$n$个顶点并有$n-1$条边的连通树。

生成森林:非连通图中所有生成树组成的非连通图。

邻接表存储(常用):

创建:

vector<pair<int,int> > g[N];
void add(int u,int v,int w=1){
    g[u].push_back({v,w});
    g[v].push_back({u,w});
}

查询:

for(int i=0;i<g[Now].size();i++){
    int Next = g[Now][i];
    // Main Code
}

链式前向星

// head[u] 和 cnt 的初始值都为 -1
void add(int u, int v) {
  nxt[++cnt] = head[u];  // 当前边的后继
  head[u] = cnt;         // 起点 u 的第一条边
  to[cnt] = v;           // 当前边的终点
}
// 遍历 u 的出边
for (int i = head[u]; ~i; i = nxt[i]) {  // ~i 表示 i != -1
  int v = to[i];
} 
// From OI Wiki

单源最短路 Dijkstra

特点:速度快。

const int M = 100010;
const int INF=1234567890;
int n,m,Start,u,v,w,dis[M];
bool vis[M];
vector<pair<int,int> > G[M];

void RunDijkstra(){
    for(int i=1;i<=n;i++)dis[i]=INF,vis[i]=0;
    priority_queue<pair<int,int> > Queue;
    Queue.push({0,Start});
    dis[Start] = 0;
    while(!Queue.empty()){
        int Now = Queue.top().second;
        Queue.pop();
        if(vis[Now])continue;
        vis[Now] = 1;
        for(int i=0;i<G[Now].size();i++){
            int Next = G[Now][i].first;
            int Weight = G[Now][i].second;
            if(vis[Weight])continue;
            if(dis[Weight]>dis[Now]+Next){
                dis[Weight] = dis[Now]+Next;
                Queue.push({-dis[Weight],Weight});
            }
        }
    }
}

时间复杂度 $O(n \log{n})$ ;

单源最短路 Bellman-Ford

特点:可判负环、负边权。

const int N = 100010;
const int INF = 1234567890;
int n,m,Start,u,v,w;
struct edge{
    int u,v,w;
};
vector<edge> Edge;
int dis[N];

bool Bellman_Ford(int Start){
    for(int i=1;i<=n;i++)dis[i] = INF;
    dis[Start] = 0;
    bool isUpdate=0;
    for(int j=0;j<=n;j++){
        isUpdate = 0;
        for(int i=0;i<Edge.size();i++){
            u = Edge[i].u,v=Edge[i].v,w=Edge[i].w;
            if(dis[u]==INF)continue;
            if(dis[u]+w<dis[v]){
                isUpdate = 1;
                dis[v] = dis[u]+w;
            }
        }
        if(!isUpdate){
            return 0;
        }
    }
    return isUpdate;
} // true:有负环 false:没有

时间复杂度 $O(nm)$

多源最短路 Floyd

特点:代码短。

for (k = 1; k <= n; k++) {
  for (x = 1; x <= n; x++) {
    for (y = 1; y <= n; y++) {
      f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
    }
  }
}

时间复杂度 $O(n^3)$ ;

拓扑排序

每次访问入度为0的节点产生的顺序。

int n,m,x,y,in[N];
vector<int> g[N];
queue<int> q;
bool vis[N];
int ans[N],top=0;
void init(){
    for(int i=1;i<=m;i++){
        cin >> x >> y;
        g[x].push_back(y);
        in[y]++,out[x]++;
    }
}
void getsorting(){
    init();
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(in[i]==0)q.push(i);
    }
    while(!q.empty()){
        int Now = q.front();q.pop();
        if(vis[Now])continue;
        vis[Now] = 1;
        ans[++top] = Now;
        for(int i=0;i<g[Now].size();i++){
            int Next = g[Now][i];
            in[Next]--;
            if(in[Next]<=0)q.push(Next);
        }
    }
}

时间复杂度 $O(n+m)$ ;

最小生成树 Kruskal

大小排序,优先选择边最小的。使用并查集记录连通块。

int n,m,x,y,z,fa[N],ans=0;
struct edge{
    int u,v,w;
    bool operator <(edge y) const{
        return w<y.w;
    }
}Edge[M];int top=0;

void init(){
    for(int i=1;i<n;i++)fa[i] = i;
}
int Find(int x){
    if(fa[x]==x)return x;
    return fa[x] = Find(fa[x]);
}
void Union(int x,int y){
    fa[Find(x)] = Find(y);
}

int main(){
    cin >> n >> m;
    init();
    for(int i=1;i<=m;i++){
        cin >> x >> y >> z;
        Edge[++top] = {x,y,z};
    }
    sort(Edge+1,Edge+1+top);
    for(int i=1;i<=top;i++){
        x = Edge[i].u, y = Edge[i].v, z = Edge[i].w;
        if(Find(x)==Find(y))continue;
        ans+=z;
        Union(x,y);
    }
    for(int i=2;i<=n;i++){
        if(Find(i)!=Find(i-1)){
            cout << "-1" << endl;
            return 0;
        }
    }
    cout << ans << endl;
    return 0;
}

时间复杂度 $O(m\log{m})$ ;

最小生成树 Prim

排序最近的

int n,m,x,y,z,ans=0,cnt;
bool vis[N];
vector<int> g[N],w[N];
struct node{
    int Weight,Next;
    bool operator <(node y) const{ // min
        return Weight>y.Weight;
    }
};
priority_queue<node> q;
void Prim(){
    q.push({0,1});
    while(!q.empty()){
        int Now = q.top().Next,W = q.top().Weight;
        q.pop();
        if(vis[Now])continue;
        vis[Now] = 1;cnt++;ans+=W;
        for(int i=0;i<g[Now].size();i++){
            int Next = g[Now][i],Weight = w[Now][i];
            if(vis[Next])continue;
            q.push({Weight,Next});
        }
    }
}
// cnt<n 则图不连通

时间复杂度 $O(n\log{n}+m)$ ;

树论

概念

空树:没有节点的树。

节点的度:一个节点还有的子树的个数。

树的度:一棵树中,最大节点的度。

叶节点:度为0的节点。

父节点、子节点、兄弟节点

二叉树:树的度为二的树

完全二叉树:除了最下层,其余饱满

满二叉树:每层都饱满

性质

二叉树第$i$层节点数最多:$2^{n-1}$ $(i\geq1)$

深度为$k$的二叉树最多有$2^k-1$个节点$(k\geq1)$

满二叉树节点数:$2^k-1$ $(k\geq1)$

最近公共祖先(倍增)

使用倍增做法求lca。

int n,m,x,y,z,Start,fa[N][50],Deep[N],M;
vector<int> g[N];
bool vis[N];

void NewTreeDfs(int Now){
    for(int i=0;i<g[Now].size();i++){
        int Next = g[Now][i];
        if(vis[Next])continue;
        vis[Next] = 1;
        Deep[Next] = Deep[Now]+1;
        fa[Next][0]=Now;
        NewTreeDfs(Next);
    }
}
void Init(){
    Deep[Start] = 1;vis[Start]=1;
    NewTreeDfs(Start);
    for(int j=1;j<=M;j++){
        for(int i=1;i<=n;i++){
            fa[i][j] = fa[fa[i][j-1]][j-1];
        }
    }
}
int Lca(int x,int y){
    if(Deep[x]<Deep[y])swap(x,y);
    for(int i=M;i>=0;i--){
        if(Deep[x]-(1<<i)>=Deep[y])x = fa[x][i];
    }
    if(x==y)return x;
    for(int i=M;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}

时间复杂度 $O(n\log{n})$ ;

树的重心

使用重心性质1:当树以结点 $v$ 为根时,任一真子树的大小均不超过原树结点数的一半。

int n,x,y,cnt[N];
vector<int> g[N];
bool vis[N],ans[N];

void dfs(int Now){
    if(vis[Now])return ;
    vis[Now] = 1;
    cnt[Now] = 1;
    ans[Now] = 1;
    for(int i=0;i<g[Now].size();i++){
        int Next = g[Now][i];
        if(vis[Next])continue;
        dfs(Next);
        cnt[Now] += cnt[Next];
        if(cnt[Next]>n/2)ans[Now]=0;
    }
    if(n-cnt[Now]>n/2)ans[Now] = 0;
} // ans 为最终答案

时间复杂度 $O(n)$ ;

树链剖分

树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。

HLD

数据结构

int stack[N],top=0;
void push(int num){
    stack[++top] = num;
}
void pop(){
    top--;
}
// size: top

队列

int queue[N],l=1,r=0;
void push(int num){
    queue[++r] = num;
}
void pop(){
    l++;
}
// size: r-l+1

链表

struct Node{
    int value;
    Node *next;
};
void InsertNode(int i, Node *p) {
    Node *node = new Node;
    node->value = i;
    node->next = p->next;
    p->next = node;
}

并查集

快速查询两个点是否在同一个集合中。

int n,fa[N];
void init(){
    for(int i=1;i<n;i++)fa[i] = i;
}
int Find(int x){
    if(fa[x]==x)return x;
    return fa[x] = Find(fa[x]);
}
void Union(int x,int y){
    fa[Find(x)] = Find(y);
}

时间复杂度 $O(1)$ ;

单调队列

求最大值使用单调降队列,求最小值使用升队列。

void monotone_max(){
    int head=1,tail=0;
    for(int i=1;i<=n;++i){
        while(head<=tail and q[tail]<=a[i])
            tail--;
        q[++tail]=a[i];
        p[tail]=i;
        while(p[head]<=i-k)
            head++;
        if(i>=k)cout << q[head] << endl;
    }
}

优先队列 std::priority_queue 使用方法:

创建:

priority_queue<int> myQueue;

添加:

myQueue.push(Num);

获取:

myQueue.top()

删除:

myQueue.pop();

判断是否为空

myQueue.empty()

获取长度

myQueue.size()

堆是一棵树,其每个节点都有一个键值,且每个节点的键值都大于等于/小于等于其父亲的键值。

int n,m,Tree[N],top=0;
void push(int num){
    Tree[++top] = num;
    int rt = top;
    while(rt>1 and Tree[(rt>>1)]<Tree[rt]){
        swap(Tree[(rt>>1)],Tree[rt]);
        rt = rt>>1;
    }
}
int front(){return Tree[1];}
void pop(){
    swap(Tree[1],Tree[top--]);
    int rt = 1;
    while(rt<=top){
        int left = rt<<1;
        int right= left+1;
        if(left<=top and Tree[left]>Tree[rt] and Tree[left]>=Tree[right]){
            swap(Tree[left],Tree[rt]);
            rt=left;
            continue;
        }
        if(right<=top and Tree[right]>Tree[rt]){
            swap(Tree[right],Tree[rt]);
            rt=right;
            continue;
        }
        return ;
    }
}

线段树

以下线段树支持区间修改区间查询。

ll n,m,op,x,y,z,a[N];
ll Tree[4*N],Lazy[4*N];
void update(ll rt){
    Tree[rt] = Tree[rt<<1]+Tree[(rt<<1)+1];
}
void updatelazy(ll rt,ll l,ll r,ll mid){
    Tree[rt<<1] += Lazy[rt]*(mid-l+1);
    Tree[(rt<<1)+1] += Lazy[rt]*(r-mid);
    Lazy[rt<<1] += Lazy[rt];
    Lazy[(rt<<1)+1] += Lazy[rt];
    Lazy[rt] = 0;
}
void Init(ll l=1,ll r=n,ll rt=1){
    if(l==r){
        Tree[rt] = a[l];
        Lazy[rt] = 0;
        return ;
    }
    ll mid = l+r>>1;
    Init(l,mid,rt<<1);
    Init(mid+1,r,(rt<<1)+1);
    update(rt);
}
void Change(ll x,ll y,ll z,ll l=1,ll r=n,ll rt=1){
    if(x<=l and r<=y){
        Tree[rt] += (r-l+1)*z;
        Lazy[rt] += z;
        return ;
    }
    ll mid = l+r>>1;
    updatelazy(rt,l,r,mid);update(rt);
    if(x<=mid) Change(x,y,z,l,mid,rt<<1);
    if(y>mid)  Change(x,y,z,mid+1,r,(rt<<1)+1);
    update(rt);
}
ll Get(ll x,ll y,ll l=1,ll r=n,ll rt=1){
    if(x<=l and r<=y){
        return Tree[rt];
    }
    ll mid = l+r>>1,ans=0;
    updatelazy(rt,l,r,mid); update(rt);
    if(x<=mid) ans+=Get(x,y,l,mid,rt<<1);
    if(y>mid)  ans+=Get(x,y,mid+1,r,(rt<<1)+1);
    return ans;
}

时间复杂度 $O(\log{n})$ ;

树状数组

注意 lowbit 函数计算。

int lowbit(int x){return x&-x;}
int n,m,op,x,y,a[N],sum[N];

void add(int x,int y){
    int tmp = x;
    while(tmp<=n){
        sum[tmp] += y;
        tmp += lowbit(tmp);
    }
}
int get(int x){
    int tmp = x,ans=0;
    while(tmp>0){
        ans+=sum[tmp];
        tmp-= lowbit(tmp);
    }
    return ans;
}
void init(){
    for(int i=1;i<=n;i++){
        add(i,a[i]);
    }
}

时间复杂度 $O(n \log{n})$ ;

二叉搜索树

集合 set 使用方法:

创建:

std::set<int> mySet;

插入元素

mySet.insert(Num);

遍历元素

for (int num : mySet) {
    // num -> ans
}

查找元素

(mySet.find(20) != mySet.end())

删除元素

mySet.erase(Num);

检测集合是否为空

mySet.empty()

获取集合长度

mySet.size()

数学

高精度

__int128 使用方法:支持高精度运算。

输出输出:

inline void read(int &n){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    n=x*f;
}
inline void print(int n){
    if(n<0){
        putchar('-');
        n*=-1;
    }
    if(n>9) print(n/10);
    putchar(n % 10 + '0');
}
#include<bits/stdc++.h>
using namespace std;
const int N = 1000;

struct jclong{
    short a[N];
    int it;
    bool is_negative;
    void init(){
        for(int i=0;i<N;i++) a[i]=0;
        it=0;
        is_negative = false;
    }
    void trim() {
        while(it > 0 && a[it] == 0) {
            it--;
        }
        if(it == 0) {
            is_negative = false;
        }
    }
    void str_to(string s){
        init();
        int start = 0;
        if(s[0] == '-') {
            is_negative = true;
            start = 1;
        }
        for(int i = s.size() - 1; i >= start; i--){
            a[++it] = (s[i] - '0');
        }
        trim();
    }
    string to_str(){
        if(it == 0) return "0";

        string s = "";
        if(is_negative) s += '-';
        for(int i = it; i > 0; i--){
            s += char(a[i] + '0');
        }
        return s;
    }
    void int_to(int s){
        init();
        if(s < 0) {
            is_negative = true;
            s = -s;
        }
        while(s > 0){
            a[++it] = s % 10;
            s /= 10;
        }
    }
    int to_int(int Mod = 100000007){
        if(it == 0) return 0;
        long long s = 0, x = 1;
        for(int i = 1; i <= it; i++){
            s = (s + a[i] * x) % Mod;
            x = (x * 10) % Mod;
        }
        if(is_negative) {
            s = (Mod - s) % Mod;
        }
        return (int)s;
    }
    void read(){
        string s;
        cin >> s;
        str_to(s);
    }
    void write(){
        cout << to_str();
    }
    bool abs_greater(const jclong& b) const {
        if(it != b.it) return it > b.it;
        for(int i = it; i >= 1; i--){
            if(a[i] != b.a[i]) return a[i] > b.a[i];
        }
        return false;
    }
    bool operator<(const jclong& b) const {
        if(is_negative != b.is_negative) {
            return is_negative;
        }
        if(is_negative) {
            return abs_greater(b);
        } else {
            if(it != b.it) return it < b.it;
            for(int i = it; i >= 1; i--){
                if(a[i] != b.a[i]) return a[i] < b.a[i];
            }
            return false;
        }
    }
    bool operator>(const jclong& b) const {
        return b < *this;
    }
    bool operator==(const jclong& b) const {
        if(it != b.it || is_negative != b.is_negative) return false;
        for(int i = 1; i <= it; i++){
            if(a[i] != b.a[i]) return false;
        }
        return true;
    }
    jclong operator+(const jclong& b) const {
        jclong c;
        c.init();
        if(is_negative != b.is_negative) {
            jclong temp = b;
            temp.is_negative = !temp.is_negative;
            return *this - temp;
        }
        c.is_negative = is_negative;
        int x = 0;
        for(int i = 1; i <= it || i <= b.it || x > 0; i++){
            x += a[i] + b.a[i];
            c.a[++c.it] = x % 10;
            x /= 10;
        }
        c.trim();
        return c;
    }
    jclong operator-(const jclong& b) const {
        jclong c;
        c.init();
        if(b.is_negative) {
            jclong temp = b;
            temp.is_negative = false;
            return *this + temp;
        }
        if(is_negative) {
            jclong temp = b;
            temp.is_negative = true;
            return *this + temp;
        }
        if(abs_greater(b)) {
            int x = 0;
            for(int i = 1; i <= it || i <= b.it; i++){
                x = a[i] - b.a[i] - x;
                if(x < 0) {
                    c.a[++c.it] = x + 10;
                    x = 1;
                } else {
                    c.a[++c.it] = x;
                    x = 0;
                }
            }
            c.trim();
        } else {
            c.is_negative = true;
            int x = 0;
            for(int i = 1; i <= it || i <= b.it; i++){
                x = b.a[i] - a[i] - x;
                if(x < 0) {
                    c.a[++c.it] = x + 10;
                    x = 1;
                } else {
                    c.a[++c.it] = x;
                    x = 0;
                }
            }
            c.trim();
        }
        return c;
    }
    jclong operator*(const int b) const {
        jclong c;
        c.init();
        if(b == 0) return c; 
        c.is_negative = is_negative ^ (b < 0);
        int abs_b = abs(b);
        int x = 0;
        for(int i = 1; i <= it || x > 0; i++){
            x += a[i] * abs_b;
            c.a[++c.it] = x % 10;
            x /= 10;
        }
        c.trim();
        return c;
    }
    jclong operator*(const jclong& b) const {
        jclong c;
        c.init();
        if(it == 0 || b.it == 0) return c; 
        c.is_negative = is_negative ^ b.is_negative;
        for(int i = 1; i <= it; i++){
            int x = 0;
            for(int j = 1; j <= b.it || x > 0; j++){
                x += c.a[i + j - 1] + a[i] * b.a[j];
                c.a[i + j - 1] = x % 10;
                x /= 10;
            }
            if(i + b.it - 1 > c.it) {
                c.it = i + b.it - 1;
            }
        }
        c.trim();
        return c;
    }
    jclong operator/(const jclong& b) const {
        jclong quotient;
        quotient.init();
        if(b.it == 0) {
            cerr << "Error" << endl;
            exit(1);
        }
        if(b.it == 1 && b.a[1] == 1 && !b.is_negative) {
            quotient = *this;
            return quotient;
        }
        quotient.is_negative = is_negative ^ b.is_negative;
        jclong dividend = *this;
        jclong divisor = b;
        dividend.is_negative = false;
        divisor.is_negative = false;
        if(dividend < divisor) {
            return quotient;
        }
        jclong temp;
        temp.init();
        int index = dividend.it;
        for(; index > 0; index--) {
            temp = temp * 10;
            temp.a[1] = dividend.a[index];
            temp.it = max(temp.it, 1);
            temp.trim();
            int count = 0;
            while(!(temp < divisor)) {
                temp = temp - divisor;
                count++;
            }
            if(quotient.it > 0 || count > 0) {
                quotient.a[++quotient.it] = count;
            }
        }
        reverse(quotient.a + 1, quotient.a + quotient.it + 1);
        quotient.trim();
        return quotient;
    }
    jclong operator%(const jclong& b) const {
        jclong remainder;
        remainder.init();
        if(b.it == 0) {
            cerr << "Error" << endl;
            exit(1);
        }
        // 取绝对值进行计算
        jclong dividend = *this;
        jclong divisor = b;
        dividend.is_negative = false;
        divisor.is_negative = false;
        if(dividend < divisor) {
            remainder = dividend;
            remainder.is_negative = is_negative; 
            return remainder;
        }
        jclong temp;
        temp.init();
        int index = dividend.it;
        // 试商法计算余数
        for(; index > 0; index--) {
            temp = temp * 10;
            temp.a[1] = dividend.a[index];
            temp.it = max(temp.it, 1);
            temp.trim();
            while(!(temp < divisor)) {
                temp = temp - divisor;
            }
        }
        remainder = temp;
        remainder.is_negative = is_negative;
        return remainder;
    }
};

快速幂

ll pow(ll x,ll y,ll Mod){
    if(y==0)return 1;
    ll res = pow(x,y/2,Mod)%Mod;
    if(y%2==0) return res*res%Mod;
    return res*res%Mod*x%Mod;
}

线性质数筛

int f[N];
void init(int n){
    for(int i=2;i<=n;i++) f[i] = 0;
    for(int i=2;i<=n;i++){
        if(f[i])continue;
        for(int j=i+i;j<=n;j+=i){
            f[j] = 1;
        }
    }
}

最大公约数

int gcd(int a, int b) {
  if(b==0)return a;
  return gcd(b,a%b);
}

排列组合

$$ A(n, k) = \frac{n!}{(n - k)!} $$

$$ C(n, k) = \frac{n!}{k! \cdot (n - k)!} $$

字符串

字符串哈希

直接判断hash值是否相等即可。

const int Smax = 26,Smin = 'a';
int gethash(string s,int Mod){
    int ans=0;
    for(int i=0;i<s.size();i++){
        ans = ans*Smax+(s[i]-Smin);
        ans %= Mod;
    }
    return ans;
}

字典树

struct node{
    int Next[128];
    int Value;
} Trie[N];int top=0;
void init(int n){
    for(int j=0;j<128;j++)Trie[n].Next[j] = 0;
    Trie[n].Value = 0;
}
void push(string s,int i=0,int rt=1){
    Trie[rt].Value ++;
    if(i>=s.size()) return ;
    if(Trie[rt].Next[s[i]]==0){
        init(++top);
        Trie[rt].Next[s[i]] = top;
    }
    push(s,i+1,Trie[rt].Next[s[i]]);
}
int get(string s,int i=0,int rt=1){
    if(i>=s.size())return Trie[rt].Value;
    if(Trie[rt].Next[s[i]]==0)return 0;
    return get(s,i+1,Trie[rt].Next[s[i]]);
}

KMP

string a,b; // a,b 下标 1~n,1~m
int Next[N],n,m;
void InitNext(string b){
    Next[1] = 0;
    int j = 0;
    for(int i=2;i<=m;i++){
        while(j>0 and b[i]!=b[j+1]){
            j = Next[j];
        }
        if(b[i]==b[j+1]){
            j++;
        }
        Next[i] = j;
    }
}
void RunKMP(string a,string b){
    int j=0;
    for(int i=1;i<=n;i++){
        while(j>0 and a[i]!=b[j+1]){
            j = Next[j];
        }
        if(a[i]==b[j+1]){
            j++;
        }
        if(j>=m){
            cout << ( i - j + 1 ) << endl; // ans
            j = Next[j];
        }
    }
}

时间复杂度 $O(n)$ ;

AC自动机

字典树 + KMP

struct node{
    int Next[26];
    int Value,Back,Left;
    int cnt;
} AC[N];int top=0;
void init(int n,int lft,int v){
    for(int i=0;i<26;i++)AC[n].Next[i] = 0;
    AC[n].Value= v;
    AC[n].Back = 0;
    AC[n].Left = lft;
    AC[n].cnt = 0;
}
void TrieAdd(string s){
    int rt=1;
    for(int i=0;i<s.size();i++){
        if(AC[rt].Next[s[i]-'a']==0){
            init(++top,rt,s[i]-'a');
            AC[rt].Next[s[i]-'a'] = top;
        }
        rt = AC[rt].Next[s[i]-'a'];
    }
    AC[rt].cnt ++ ;
}
void ACinit(){
    queue<int> q;
    AC[1].Back = 0;
    for(int i=0;i<26;i++){
        if(AC[1].Next[i]==0)continue;
        AC[AC[1].Next[i]].Back = 1;
        q.push(AC[1].Next[i]);
    }
    while(!q.empty()){
        int rt = q.front();q.pop();
        int j = AC[AC[rt].Left].Back;
        while(j>0 and AC[j].Next[AC[rt].Value]==0){
            j = AC[j].Back;
        }
        if(j!=0){
            j = AC[j].Next[AC[rt].Value];
        }else j = 1;
        AC[rt].Back = j;
        for(int i=0;i<26;i++){
            if(AC[rt].Next[i]!=0){
                q.push(AC[rt].Next[i]);
            }
        }
    }
}
int RunKMP(string a){
    int j=1,ans=0;
    for(int i=0;i<a.size();i++){
        while(j>0 and AC[j].Next[a[i]-'a']==0){
            j = AC[j].Back;
        }
        if(AC[j].Next[a[i]-'a']!=0){
            j = AC[j].Next[a[i]-'a'];
        }else j=1;
        ans+=AC[j].cnt;
        AC[j].cnt = 0;
    }
    return ans;
}

Manacher

int n,a[N],Len[N];
void InitStrToInt(string s){
    n=1;a[n]=28;a[++n]=28;
    for(int i=0;i<int(s.size());i++){
        a[++n]=int(s[i]-'a'+1);
        a[++n]=28;  // add a wall
    }
}
int Manacher_GetMaxhw(){
    int j=0,ans=0; // j : max len effect
    for(int i=1;i<=n;i++){
        int NowAns=1;
        if(Len[j]+j>i)
            NowAns = min(Len[j-(i-j)],Len[j]+j-i);
        while(a[i-NowAns]==a[i+NowAns] 
         and (NowAns+i<=n and i-NowAns>0)){
            NowAns++;
        }
        Len[i]=NowAns;
        if(Len[j]+j<i+Len[i]){
            j = i;
        }
        ans = max(ans,Len[i]);
    }
    return ans-1;
}

时间复杂度 $O(n^2)$ ;


声明:本博客由作者原创,转载请标明出处。