首页 > 其他分享 >模板

模板

时间:2023-08-12 17:26:05浏览次数:30  
标签:存储 return int mid 节点 模板 dis

1.线性筛模板

void get_primes(int n)
{
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= n / i; j ++)
        {
            st[primes[j]*i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}

2.二分模板

版本1

当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。(右边满足的最小值)

C++ 代码模板:

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

版本2

当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。(左边满足的最大值)

C++ 代码模板:

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

3.并查集

(1)朴素并查集:

int p[N]; //存储每个点的祖宗节点

// 返回x的祖宗节点
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;

// 合并a和b所在的两个集合:
p[find(a)] = find(b);

(2)维护size的并查集:

int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

// 返回x的祖宗节点
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
    p[i] = i;
    size[i] = 1;
}

// 合并a和b所在的两个集合,顺序不能反,保证新加入后祖宗不变,节点数对应:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);

(3)维护到祖宗节点距离的并查集:

int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离

// 返回x的祖宗节点,带权并查集
int find(int x)
{
    if (p[x] != x)
    {
        int u = find(p[x]);
        d[x] += d[p[x]];
        p[x] = u;
    }
    return p[x];
}

// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
    p[i] = i;
    d[i] = 0;
}

// 合并a和b所在的两个集合
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量c

4.字符串哈希

核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果

typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64

// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];
    p[i] = p[i - 1] * P;
}

// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

5.堆

// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;

// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);

6.KMP

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}

// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j];
        // 匹配成功后的逻辑
    }
}

7.djikstra优化版

时间复杂度 O(mlogn), n 表示点数,m 表示边数
typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

int dij()
{
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>>q;
    q.push({dis[1],1});
    while(q.size())
    {
        PII t=q.top();
        q.pop();
        int tt=t.second;
        

        if(st[tt]) continue;
        st[tt]=true;
        for(int i=h[tt];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>dis[tt]+w[i])
            {
                dis[j]=dis[tt]+w[i];
                q.push({dis[j],j});
            }
        }
    }
    return dis[n];

}

8.spfa

时间复杂度 平均情况下 O(m),最坏情况下 O(nm), n 表示点数,m 表示边数
int n;      // 总点数
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储每个点到1号点的最短距离
bool st[N];     // 存储每个点是否在队列中

void add(int a,int b,int c)
{
	w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	queue<pair<int,int>>q;
	q.push({dis[1],1});
	st[1]=true;
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		int tt=t.second;
		st[tt]=false;
		for(int i=h[tt];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dis[j]>dis[tt]+w[i])
			{
				dis[j]=dis[tt]+w[i];
				if(!st[j])
				{
					st[j]=true;
					q.push({dis[j],j});
				}
			}
		}
	}
	return dis[n];
}

9.kruskal

时间复杂度是 O(mlogm), n 表示点数,m 表示边数
点的成本可以看作是点到某个某个特别点的边权
int n, m;       // n是点数,m是边数
int p[N];       // 并查集的父节点数组

struct Edge     // 存储边
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }

}edges[M];

int find(int x)     // 并查集核心操作
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集
    
    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
    
        a = find(a), b = find(b);
        if (a != b)     // 如果两个连通块不连通,则将这两个连通块合并
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }
    
    if (cnt < n - 1) return INF;
    return res;

}

10.有向图的拓扑序列

序列保存在orz中
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool top()
{
    

    for(int i=1;i<=n;i++)
    {
        if(d[i]==0) q.push(i);
    }
    while(q.size())
    {
        int t=q.front();
        //cout<<t<<endl;
        orz[cnt++]=t;
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            d[j]--;
            if(d[j]==0) q.push(j);
        }
    }
    if(cnt<n) return 0;
    else return 1;

}

11.floyd算法

时间复杂度是 O(n3), n 表示点数
初始化:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// 算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

12.Bellman-Ford算法

时间复杂度 O(nm), n 表示点数,m 表示边数
注意在模板题中需要对下面的模板稍作修改,加上备份数组,详情见模板题。

int n, m;       // n表示点数,m表示边数
int dist[N];        // dist[x]存储1到x的最短路距离

struct Edge     // 边,a表示出点,b表示入点,w表示边的权重
{
    int a, b, w;
}edges[M];

// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    // 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }
    
    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];

}

13.RMQ(ST表)--区间最值查询

区间查询用到st表,只能操作静态表,需要修改时,需要额外操作
首先定义dp[i,j]为以第i个数为起点,长度为2^j的一段区间中的最大值,显然状态转移为
dp[i,j] = max(dp[i,j-1],[i+2^(j-1),j-1]);
查询时把区间分成前后都为长度为len=r-l+1的区间,/*k为区间的最大倍增数*/,左边是(l,2^k),右边是(r-2^k+1,r)
结果为 max(dp[l][k],dp[r-(1<<j)+1],[k])
换底公式:k=log2(n)=log(n)/log(2);  math里的函数
初始化
for(int j=0;j<M;j++)
	for(int i=1;i+(1<<j)-1<=n;i++)//中间是区间边界
		if(!j) dp[i][j] = w[i];
		else dp[i][j] = max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
查询
    return max(dp[l][k],[r-(1<<k)+1][k]);

14.线段树

结构体储存数的信息,比起数组,在操作传参时避免传参时过多而出错
struct Segment_Tree 
{
    int l,r;
    LL sum;  //这里可以维护任何满足区间加法的信息,这里就用区间求和了
    //根据情况tag标记
}tr[4 * N];   //要开四倍空间

函数1———build
void build (int u,int l,int r) //当前正在下标为u的点,这个点表示的区间是[l,r]
{ 
    if (l == r) 
    {
        tr[u] = {l,r,a[l]};/a[i]为各个点的信息
        return ;
    }
    tr[u] = {l,r};  //记得存储当前点表示的区间,否则你会调上一整天
    int mid = l + r >> 1;
    build (u << 1,l,mid),build (u << 1 | 1,mid + 1,r);  //u << 1就是u * 2,u << 1 | 1就是u * 2 + 1
    push_up (u);  //push_up函数的意思是把某一个节点的信息有他的子节点算出来
}
函数2.push_up
void push_up (int u) //这里只有区间和,区间和就是由一个点的左右子节点的和相加
{  
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;//向下递归更新
}
函数3.modify
	1.单点修改
	思路: 我们通过二分查找的形式找到要修改的点,然后把找的过程上的链都修改一下。时间复杂度 O(logn)
	void modify (int u,int x,int d) //当前这个点是下标为u的点,要把第x个数修改成d
    {   
    	if (tr[u].l == tr[u].r) 
        {
        	tr[u].sum = d;   //如果当前区间只有一个数,那么这个数一定是要修改的。
        	return ;
    	}
    	int mid = tr[u].l + tr[u].r >> 1;
    	if (x <= mid) modify (u << 1,x,d);  //如果在左边就递归修改左半边区间
    	else modify (u << 1 | 1,x,d);    //如果在右边就递归修改右半边区间
    	push_up (u)   //记得更新信息
	}
	2.区间修改
     思路: lazy标记法用它统一记录区间的修改,而不是一个个修改区间内的值
     void push_down (int u) //下传标记函数
    { 
    	auto &root = tr[u],&left = tr[u << 1],&right = tr[u << 1 | 1];  //加了引用符号只要修改这个变量就会修改他被赋值的变量
    	if (root.sum_tag) //有懒标记才下传
    	{  
        	left.tag += root.tag,left.sum += (LL)(left.r - left.l + 1) * root.tag;  //这里的子节点要加上懒标记,因为这里懒标记的意思是不包括当前节点
        	right.tag += root.tag,right.sum += (LL)(right.r - right.l + 1) * root.tag;  //同理
        	root.tag = 0;  //懒标记记得清零
    	}
	}
	void modify (int u,int l,int r,int d) //当前遍历到的点下标是u,要将区间[l,r]增加d
	{ 
    	if (l <= tr[u].l && tr[u].r <= r) 
    	{
        	tr[u].sum += (LL)(tr[u].r - tr[u].l + 1) * d;
        	tr[u].sum_tag += d;  //这里我才用了y总的懒标记的意思,一个点所有的子节点都加上d,而前一行时加上根节点的数,因为不包括根节点。
        	return ;
    	}
    	push_down (u);  //一定要分裂,只要记牢在递归左右区间之前,就要分裂
    	int mid = tr[u].l + tr[u].r >> 1;  //注意时tr[u].l和tr[u].r
    	if (l <= mid) modify (u << 1,l,r,d);  //左边有修改区间,就递归左半边
    	if (r >= mid + 1) modify (u << 1 | 1,l,r,d);  //右边有修改区间,就递归右半边
    	push_up (u);  //要记得要把这个点的信息更新一下
}
       

15.LCA--最近公共祖先

(1).向上标记法( O(n) )
(2).倍增
fa[i,j]标识从i开始,向上跳2^j步所能走到的节点,0<=j<=logn
depth[i]表示深度
步骤:
[1].先让两个点跳到同一层
[2].让两个点同时往上跳,一直跳到它们公共祖先的下一层
预处理O(nlogn)
查询O(logn)

(3).tarjan( O(n + m) )

在深度优先遍历的同时, 将所有点分成三类

[1].已经遍历过的点, 且回溯过的点

[2].正在搜索的分支

[3].还未搜索到的点

倍增法

//倍增处理
void bfs()
{
    //memset(dis, 0x3f, sizeof dis);
    memset(depth, 0x3f, sizeof depth);
    queue<int> q;
    depth[0] = 0, depth[1]=1;//并非是1,是root
    //dis[1] = 0;
    q.push(1);
    while(q.size())
    {
        int u = q.front();
        q.pop();
        for(auto &[v, w] : g[u])
        {
            if(depth[v]>depth[u] + 1)
            {
                depth[v] = depth[u] + 1;
                q.push(v);
                fa[v][0] = u;
                for(int k = 1; k <= 13; k ++)
                    fa[v][k]=fa[fa[v][k-1]][k-1];
                dis[v] = dis[u] + w;//权值, 到公共祖先的距离
            }
        }
    }
}
//倍增查询
int lca(int a, int b)
{
    if(depth[a] < depth[b]) swap(a, b); //a在下面
    for(int k = 13; k >= 0; k --)
        if(depth[fa[a][k]] >= depth[b])
            a=fa[a][k];//跳到同一层
    if(a == b) return a;        
    for(int k = 13; k >=0 ; k --)
        if(fa[a][k] != fa[b][k])
            a =  fa[a][k], b = fa[b][k];//一起向上跳,跳到公共祖先的下一层
    return fa[a][0];
}

tarjan

int find(int x)//合并祖先节点
{
    if(x != p[x]) p[x] = find(p[x]);
    return p[x];
} 
void tarjan(int u)//st[u]等于1,2可以直接合在一起,因为每次遍历时都会先对子节点进行标记
{
    //cout << u << endl;
    st[u] = 1;
    for(auto &[v, w] : g[u])
    {
        if(!st[v])
        {
            dis[v] = dis[u] + w;
            tarjan(v);
            p[v] = u;
        }
    }
    for(auto &[v, id] : query[u])//离线做法的体现
        if(st[v] == 2) 
            res[id] = dis[u] + dis[v] - dis[find(v)] * 2;
            //cout << id <<  ' ' << dis[u] << ' ' << dis[v] << ' ' << res[id] << endl;
    st[u] = 2;
}

16.树状数组

1.快速求前缀和O(log n)————可以和差分数组联系起来(区间和的查询) //(a1+a2+……+an)=(b1+b2+……+bn)*(n+1)-(1*b1+2*b2+……nbn)
2.修改某一个数O(log n)    树状数组不能求区间最值
主要函数 :int lowbit(int x) { return x & - x;}
修改:增加、建树
void add(int x, int c)
{
    for (int i = x; i <= n; i += lowbit(i)) //lowbit(i)是i后需要修改的前缀和区间
        tr[i] += c;
}
查询:
LL query(int x)
{
    LL res = 0;
    for (int i = x; i; i -= lowbit(i))
        res += c[i];
    return res;//返回前缀和
}

17.匈牙利算法

时间复杂度O(nm),实际远小于

int n1, n2;     // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N], e[M], ne[M], idx;     // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N];       // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N];     // 表示第二个集合中的每个点是否已经被遍历过

bool find(int u)
{
    for (auto v : g[u])
    {
        if (!st[v])
        {
            st[v] = true;
            if (match[v] == 0 || find(match[v]))
            {
                match[v] = u;
                return true;
            }
        }
    }

    return false;
}
// 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点
int res = 0;
for (int i = 1; i <= n1; i ++ )
{
    memset(st, false, sizeof st);
    if (find(i)) res ++ ;
}

18.染色法判断二分图

int n;      // n表示点数
int h[N], e[M], ne[M], idx;     // 邻接表存储图
int color[N];       // 表示每个点的颜色,-1表示未染色,0表示白色,1表示黑色

// 参数:u表示当前节点,c表示当前点的颜色
bool dfs(int u, int c)
{
    color[u] = c;
    for(auto v : g[u])
    {
        if (color[v] == -1)
        {
            if (!dfs(v, !c)) return false;
        }
        else if (color[v] == c) return false;
    }

    return true;
}

bool check()
{
    memset(color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!dfs(i, 0))
            {
                flag = false;
                break;
            }
    return flag;
}

19.欧拉函数

int phi(int x)
{
    int res = x;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);

    return res;
}

20.试除法求所有约数

vector<int> get_divisors(int x)
{
    vector<int> res;
    for (int i = 1; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res.push_back(i);
            if (i != x / i) res.push_back(x / i);
        }
    sort(res.begin(), res.end());
    return res;
}

标签:存储,return,int,mid,节点,模板,dis
From: https://www.cnblogs.com/ZouYua/p/17625084.html

相关文章

  • 【模板】modint
    modintbycjlstructm{ intx;m(into=0){x=o;}m(lllo){x=o%mod;}m&operator+=(ma){return(x+=a.x)%=mod,*this;}m&operator-=(ma){return(x+=mod-a.x)%=mod,*this;} m&operator*=(ma){return(x=1ll*x*a.x%mod),*this;}m&operator^=(intb){ma=*thi......
  • VUE使用模板页面并预留子页面区域
    1.新建模板页面MainLayout.vue,并在template里面防止标签用于嵌入子页面内容<template>'''其他页面内容'''<router-view></router-view>'''其他页面内容'''</template>2.在router的index.js中设置子路由,其中DailyData......
  • tzoj1471 wall(凸包模板题)
    题目大意n个点构成的城堡,给出每个点的坐标。若要修建距离城堡最近距离为L的城墙,问城墙的最短长度。凸包模板题,用Andrew算法求出凸包然后加上半径为L的圆的周长即可。Andrew算法首先对所有点按照y大小进行升序排序,如果y相同就按照x大小升......
  • 最小生成树模板
    prim算法算法思想:每次选定未进入集合中和集合距离最小的点,用该点更新其他点到集合的距离,直到可以判断出不存在最小生成树或所有点均已进入集合下面虽然是两种写法,但是记忆时最好还是按照算法的思路来实现,也就是第2个代码。虽然会多一些边界处理,但是只要我们理解了算法思想即使......
  • 算法模板
    【DFS】classSolution{public:intn;intpath[10000];boolst[10000];voiddfs(intu){if(u==n){for(inti=0;i<n;++i)cout<<path[i]<<"";cout<<endl;return;......
  • 基于模板匹配算法的车牌数字字母识别matlab仿真,带GUI界面
    1.算法理论概述       随着交通工具的普及,车辆数量快速增长,车辆管理变得越来越重要。在车辆管理中,车牌号码的自动识别是一个重要的环节。从传统的手工识别,到现在的自动化识别,车牌识别技术已经成为了一个热门的研究领域。其中,数字字母识别是车牌识别的重要组成部分。本文......
  • SOLIDWORKS工程图模板制作
    为什么要制作工程图模板SOLIDWORKS软件以其优良的技术和市场表现,成为CAD领域一颗耀眼的明星,拥有强大的功能。为了实现更规范、更快捷、更方便、更准确的绘图,制作工程图模板是必要的。SolidWorks工程图的优势在于零件模型的尺寸与工程图相关联,只需修改模型尺寸,工程图中的尺寸也会相......
  • 借助Aspose.Html 将 HTML 模板与 XML 或 JSON 合并
    在现代网络开发中,内容和表示的分离是一个基本原则。HTML模板提供了一种定义网页结构和布局的便捷方法,而JSON和XML数据格式通常用于存储和传输结构化信息。结合这些技术,开发人员可以根据外部源的数据动态生成HTML内容。在这篇博文中,我们将探讨如何在Java中将HTML模板与JS......
  • 洛谷 P3387 【模板】缩点
    在洛谷中查看所有思考都在代码,可以结合代码思考谢谢~#include<bits/stdc++.h>usingnamespacestd;constintN=1e4+5;intn,m,val[N];intdfn[N],low[N],num,col[N],cnt;//col记录每个点属于哪个联通分量//num记录遍历时间cnt记录缩点完后有多少个点in......
  • java根据excel模板填充数据并导出
    部分代码片段<!--导出导入excel--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.12</version></dependency>......