算法
算法题解
独孤醉人
这个作者很懒,什么都没留下…
展开
-
Treap
普通平衡树#include <bits/stdc++.h>using namespace std;const int N=1e5+10,INF=1e8;struct node{ int l,r; int key,val; int cnt; int size;}tr[N];int root,idx;void pushup(int p){ tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].c原创 2021-07-22 19:48:23 · 102 阅读 · 0 评论 -
耍杂技的牛
原题链接结论:按照牛的体重和强壮值之和从小到大排序得到的序列是危险值的最大值最小的排列方式证明:这道题我们可以用转换的思路来证明,即:如果存在最终答案的序列中存在逆序的情况,我们一定一刻通过把逆序倒过来的方式使答案进一步减小代码#include <bits/stdc++.h>using namespace std;typedef long long ll;typedef pair<int,int>PII;const int N=5*1e5+10;int n;PII原创 2021-01-25 11:08:16 · 187 阅读 · 0 评论 -
排队打水
原题链接结论:时间短的小朋友先打水,时间长的小朋友后打水证明:我们假设最终的答案中有逆序,如果将逆序倒过来,那么一定可以得到一个更小的答案代码#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=1e5+10;int n;int a[N];ll res=0;int main(){ scanf("%d",&n); for(int i=0;i<n;原创 2021-01-25 10:16:40 · 133 阅读 · 0 评论 -
Huffman树——合并果子
原题链接解析这道题是一个典型的Huffman树的题目对于任何一种构造都可以转化成一棵树,这道题相当于是求跟结点的权制最小因此这道题可以用Huffman树来进行解决,权值越小的点离树根越远越好,权值越大的点离树根越近越好因此可以用小根堆来实现这个过程代码#include <bits/stdc++.h>using namespace std;int n;priority_queue<int,vector<int>,greater<int> >v原创 2021-01-25 09:47:53 · 168 阅读 · 0 评论 -
区间贪心
区间选点区间分组最大不相交区间数量区间覆盖原创 2021-01-24 18:30:55 · 224 阅读 · 0 评论 -
数位统计DP——计数问题
原题传送门解析啊啊啊!这道题卡了一下午才搞出来,巨多边界问题!对于直接求解比较困难的问题,可以转化成前缀的方式求解,这道题也是一样,可以求解1~b 中数x出现的数量,减去1~a-1中数x出现的数量,间接得到答案假定一个数按位表示成abcdefg,指针指到d这一位,统计这种情况下的答案当x不为0的时候1、当d这一位枚举x的时候,abc这三位可以从000~abc-1,efg这三位可以从000~999,也就是1000=103种选择2、当abc这三位就枚举abc的时候,当d<x,有0种选择3、原创 2021-01-24 16:21:05 · 214 阅读 · 0 评论 -
记忆化搜索——滑雪
原题链接很简单的一个DP,重要的是记忆化的过程不多说了看代码吧代码#include <bits/stdc++.h>using namespace std;const int N=310;int n,m;int g[N][N];int f[N][N];int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};int dp(int i,int j){ if(f[i][j]!=-1) return f[i][j]; f[i][j]=1;原创 2021-01-24 11:27:20 · 77 阅读 · 0 评论 -
树形DP——没有上司的舞会
原题链接分析状态表示:f[u][0]:表示以u为根结点但是不选择u快乐值的最大值f[u][1]:表示以u为根结点且选择u的快乐值的最大值状态转移:f[u][0]:对于这种情况我们可以选择u结点的孩子也可以不选择u结点的孩子,因此需要取两者的最大值f[u][1]:对于这种情况,我们已经选择了根结点,那么我们就不能再次选择根结点的所有子结点,因此我们需要把u结点的所有子结点中不选子结点的情况相加代码#include <bits/stdc++.h>using namespace s原创 2021-01-24 10:35:18 · 68 阅读 · 0 评论 -
状压DP——蒙德里安的梦想/最短hamilton路径
状压DP简单来说就是用二进制的方式来压缩状态具体的题目会有不同的表示方法蒙德里安的梦想首先分析这个问题,我们现在行上放小方格,在行上放完之后,那么纵向的小方格就只有一种放的方式。因此!只需要求出横向放的方式就可以了状态表示:f[i][j]为数量,i表示枚举到第i列,j表示上一列伸出来的多少个小方格,j是用二进制表示的,为1的位表示当前位有小方格伸出来对于j这一维度的状态表示就是状压DP接下来再想一下状态转移首先就是不能存在冲突,就是说第i列和第i-1列不能同时伸出一个小方格,这里可以用与(&a原创 2021-01-23 17:27:42 · 176 阅读 · 0 评论 -
计数DP——整数划分
原题传送门分析根据题意这道题是在求组合,不考虑顺序的这道题可以转化为一个完全背包问题,背包容量是n,有n种物品,这些物品的权重是1~n,每种物品可以取无限次。这不就是之前做过的完全背包问题吗!背包问题博客链接完全背包问题中统计的是总价值的最大值,这道题是计数,我们只需要将原来的状态方程中的取最大值操作更改成累加操作,就可以求出所有的方案数这道题也可以用类似于完全背包的方式进行优化,详细的过程在背包问题的博客中已经有了(我懒的写了),我们可以用类似的方式将二维的优化成一维的代码#include原创 2021-01-22 17:48:34 · 209 阅读 · 0 评论 -
区间DP——石子合并
原题传送门代码#include <bits/stdc++.h>using namespace std;const int N=310;int s[N];int f[N][N];int main(){ int n; cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++) s[i]+=s[i-1]; //求前缀和 for(int len=2原创 2021-01-22 11:57:34 · 78 阅读 · 1 评论 -
线性DP
数字三角形就很水一道DP,不多说了直接贴代码了#include <bits/stdc++.h>using namespace std;const int N = 105, INF = 1e9;int x[N][N];int dp[N][N];int main(){ int n; cin >> n; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++)原创 2021-01-07 20:00:27 · 92 阅读 · 0 评论 -
博弈论——Nim游戏
Nim游戏通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗,不能不拿,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。在这里定义两个状态必胜状态:如果这个状态可以转移到必败状态,那么这么状态就是必胜状态必败状态:如果这个状态转移到的所有状态都是必胜状态,那么这个状态就是必败状态对于这个游戏给出结论当a1^ a2 ^… ^ an=0,则先手必败,否则先手必胜Nim游戏#include <bi原创 2021-01-04 18:06:50 · 250 阅读 · 1 评论 -
容斥原理
容斥原理能被整除的数这道题是典型的利用容斥原理的题目由于题目中所给的都是质数,因此在求交集的时候可以直接相乘求交集这里判断哪个数选到哪个数没有选到可以DFS也可以二进制枚举这里给出二进制枚举的做法#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=20;int n,m;int p[N];int main(){ cin>>n>>m;原创 2021-01-04 13:44:24 · 194 阅读 · 0 评论 -
高斯消元法解异或线性方程组
高斯消元法对于一组线性方程组,枚举每一列进行如下步骤:1、找到首元非零行2、将这一行交换到第一行3、将这一行的第一个数变成1,对当前这一行进行操作,不涉及矩阵的初等变换4、将下面所有行的当前列全部消成0,利用矩阵的初等变换对于原异或方程组进行变换后如果得到的矩阵是一个完美的上三角矩阵,则说明方程组有唯一解否则会出现两种情况:1、推出矛盾,即:零==非零,这种情况下表示无解2、否则,即原异或方程组中有多余的方程,此时有无穷多解高斯消元解异或线性方程组#include <bits/原创 2021-01-03 10:04:06 · 987 阅读 · 0 评论 -
组合数学——求组合数
对于求组合数,要根据所给数据范围来选择合适的算法求组合数 I这道题中所给的数据范围适合用打表的方法直接暴力求解先用4e6的复杂度预处理出所有的情况,再用1e4的复杂度完成询问即可#include <bits/stdc++.h>using namespace std;const int N = 2010;const int MOD = 1e9 + 7;int c[N][N];void init(){ for (int i = 0; i < N; i++)原创 2021-01-02 21:13:58 · 220 阅读 · 0 评论 -
高斯消元法解线性方程组
高斯消元法对于一组线性方程组,枚举每一列进行如下步骤:1、找到绝对值最大的一行2、将这一行交换到第一行3、将这一行的第一个数变成1,对当前这一行进行操作,不涉及矩阵的初等变换4、将下面所有行的当前列全部消成0,利用矩阵的初等变换对于原线性方程组进行变换后如果得到的矩阵是一个完美的上三角矩阵,则说明方程组有唯一解否则会出现两种情况:1、推出矛盾,即:零==非零,这种情况下表示无解2、否则,即原线性方程组中有多余的方程,此时有无穷多解高斯消元解线性方程组#include <bits原创 2021-01-01 15:47:53 · 3834 阅读 · 2 评论 -
中国剩余定理
中国剩余定理中国剩余定理给出了以下的一元线性同余方程组:有解的判定条件,并用构造法给出了在有解情况下解的具体形式。中国剩余定理说明:假设整数m1,m2, … ,mn两两互质,则对任意的整数:a1,a2, … ,an,方程组(S)有解,并且通解可以用如下方式构造得到:设是整数m1,m2, … ,mn的乘积,并设是除了mi以外的n- 1个整数的乘积。设的数论倒数( 为 模 意义下的逆元)方程组(S)的通解形式为在模M的意义下,方程组(S)只有一个解:表达整数的奇怪方式#include &原创 2020-12-24 14:56:01 · 375 阅读 · 0 评论 -
扩展欧几里得算法
基本知识裴蜀定理:对于任意一对正整数ab,则一定存在xy,是的ax+by=gcd(a,b)扩展欧几里得算法就是一种构造xy的算法例题扩展欧几里得算法#include <bits/stdc++.h>using namespace std;int exgcd(int a, int b, int &x, int &y) //在欧几里得算法的基础上进行扩展,算出xy{ if (!b) { x = 1, y = 0; retu原创 2020-12-23 20:26:47 · 190 阅读 · 0 评论 -
背包问题
0-1背包:每件物品最多只能用一次完全背包:每件物品有无限个多重背包:每件物品的数量有限制分组背包:有很多组,每组物品中只能选一个01背包问题#include <bits/stdc++.h>using namespace std;const int N = 1010;int w[N], v[N];int f[N][N]; //f(n,v)表示从前n个物品中选择,体积不超过v总价值的最大值//对于每一个f(i,j)都可以分为两个集合,含有i,和不含有i,即从1~i中选择或者1~原创 2020-12-07 20:39:56 · 127 阅读 · 0 评论 -
欧拉函数
φ(n):表示1~n中与n互质的数的个数证明的过程是基于容斥原理进行的第一步:去掉1~n中所有p1、p2…pk的倍数第二步:加上所有第一步去重了的倍数欧拉函数#include <bits/stdc++.h>using namespace std;int main(){ int n; cin >> n; while (n--) { int a; cin >> a; int re原创 2020-12-04 20:14:13 · 516 阅读 · 0 评论 -
约数
试除法求约数#include <bits/stdc++.h>using namespace std;vector<int> get(int n){ vector<int> vis; for (int i = 1; i <= n / i; i++) { if (n % i == 0) { vis.push_back(i); if (i != n / i)原创 2020-12-03 15:10:37 · 221 阅读 · 0 评论 -
质数
试除法判定质数#include <bits/stdc++.h>using namespace std;typedef long long ll;int n;int main(){ cin >> n; while (n--) { int m; cin >> m; if (m < 2) cout << "No" << endl;原创 2020-11-28 19:38:10 · 245 阅读 · 0 评论 -
二分图
一个图是二分图当且仅当一个图不含有奇数环(环中边的数量是奇数)二分图:可以将图分成两个集合,仅仅在集合之内有边,集合之内没有边原创 2020-11-06 21:11:35 · 92 阅读 · 0 评论 -
最小生成树基础算法
PrimPrim算法求最小生成树与dijkstra算法类似,dijkstra算法是计算一个节点到其他节点的最短路,Prim算法是需要维护节点到一个集合的距离最小值,优化方式也是类似的,只需要用堆来维护距离即可#include <bits/stdc++.h>using namespace std;const int N = 510, INF = 0x3f3f3f3f;int n, m;int g[N][N]; //邻接矩阵存图int dist[N]; //距离数组bool st.原创 2020-11-06 20:56:13 · 202 阅读 · 0 评论 -
最短路基础算法
朴素版dijkstra主要用于对稠密图的处理,即:m>=n2对于稠密图,用邻接矩阵进行存储Dijkstra求最短路 I#include <bits/stdc++.h>using namespace std;const int N = 510;int n, m;int g[N][N]; //用邻接矩阵存储图int dist[N]; //用来存储当前的最短距离是多少bool st[N]; //表示当前的这个点的最短距离是不是已经被确定了int dijkstra(){.原创 2020-11-04 17:42:19 · 252 阅读 · 0 评论 -
图的拓扑序列
若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。拓扑序列都是针对有向图而言的将图中的节点按拓扑序列排好后,所有的边都是从前指向后的,没有反向边只要存在环,则一定没有拓扑序列一个有向无环图一定存在一个拓扑序列(有向无环图也叫拓扑图)所有入度为0的点都有可能引导一个拓扑序列一个有向无环图一定存在一个入度为0的点有向图的拓扑序列#include <bits/stdc++.h>using namespace std;原创 2020-11-03 19:49:31 · 6204 阅读 · 0 评论 -
图的遍历
图中点的层次#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int n, m;int h[N], e[N], ne[N], idx;int d[N]; //表示距离queue<int> vis;void add(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx++;}int bfs(){ memset(原创 2020-11-03 19:05:15 · 145 阅读 · 0 评论 -
树的遍历
树的重心#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10, M = N * 2;int n;int h[N], e[M], ne[M], idx; //单链表操作,h数组表示头结点int ans = N; //存答案bool st[N]; //检查这个节点有没有遍历过void add(int a, int b) //邻接表存图原创 2020-11-03 18:48:08 · 163 阅读 · 0 评论 -
BFS宽度优先搜索
走迷宫#include <bits/stdc++.h>using namespace std;typedef pair<int, int> PII;const int N = 110;int g[N][N]; //保存地图int d[N][N]; //保存到起点的距离的距离int n, m;int bfs(){ queue<PII> vis; memset(d, -1, sizeof(d)); //将距离初始化为-1,表示这个点没走过原创 2020-10-30 20:37:43 · 184 阅读 · 0 评论 -
哈希表
模拟离散表#include <bits/stdc++.h>using namespace std;const int N = 200003, null = 0x3f3f3f3f;//N表示哈希表要映射的范围,一般为1e5左右//根据不同的方法给定的的N值有所不同//开放寻址法一般为范围的2-3倍,拉链法1倍即可//N的取值一般为一个大于指定范围的素数,数学上可以证明这种方式是可能发生冲突最少的int h[N];//find函数是开放寻址法的核心,一般哈希表的时间复杂度都为O(1原创 2020-10-27 22:20:08 · 161 阅读 · 0 评论 -
DFS深度优先搜索(一定要想好搜素顺序)
排列数字#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=10;int path[N]; //用来存方案int st[N]; //用来检查哪一个数被用过int n;void dfs(int u){ //表示深搜到最后了,即可以输出结果 if(u==n) { for(int i=0;i<n;i++) cout<原创 2020-10-14 20:21:07 · 523 阅读 · 0 评论 -
堆
基本知识存储:定义一个数组,第一个节点表示根节点对于任意一个节点x,2x为其左儿子,2x+1为其右儿子堆是一个完全二叉树,除最后一行的元素外,其他元素均非空,最后一行从左到右一次排布对于堆中的任意一个元素,其小于其两个儿子,所以根节点为最小值基本操作:down操作:对大元素进行下移up操作:对小元素进行上移功能:1、插入一个属2、取出最小值3、删除最小值4、修改任意一个元素5、删除任意一个元素题目堆排序#include <bits/stdc++.h>using原创 2020-10-12 18:35:38 · 92 阅读 · 0 评论 -
并查集
分析并查集的功能1、完成两个集合的快速合并2、询问一个元素是否在一个集合当中可以在近乎O(1)的时间复杂度中完成这两个操作基本原理将每一个集合用一棵树来表示,树根的编号表示集合每一个节点存储它的父节点,根节点存储自己本身,因此只有根节点的父节点为其本身合并两个集合时,只需要将一个集合视作另一个集合的儿子即可优化当由一个节点出发,找到其根节点的时候,将该路径上的每一个点都直接指向根节点,进行路径压缩,降低树的高度,增加了树的宽度p[N] //该数组保存节点的父节点if(p[x]==x)原创 2020-10-11 13:04:25 · 64 阅读 · 0 评论 -
trie树统计字符串
Trie字符串统计分析Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。基本性质:1、根节点不保存字符2、从根到该节点过程中所经过的字符串为词典中字符串的唯一前缀。3、实现快速查找,时间复杂度为O(n)代码#include <bits/stdc++.h>using namesp原创 2020-10-11 11:37:55 · 256 阅读 · 0 评论 -
kmp字符串算法
kmp(题目连接)分析简化字符串匹配的暴力解法通过求解ne数组,找到每一个字符之前的串中前缀==后缀的最大长度每次 j 指针向右一定ne[j]位即可省去不必要的循环代码#include <bits/stdc++.h>using namespace std;const int N = 1e5+10, M = 1e6+10;char p[N], s[M];int ne[N];int main(){ int n, m; cin >> n >&原创 2020-10-05 17:51:22 · 145 阅读 · 0 评论 -
大等于n的最小完全平方数 及 二分总结
问题描述输出大等于n的最小的完全平方数。若一个数能表示成某个自然数的平方的形式,则称这个数为完全平方数Tips:注意数据范围输入格式一个整数n输出格式大等于n的最小的完全平方数代码#include <bits/stdc++.h>using namespace std;typedef long long ll;int main(){ ll n; cin >> n; if (n <= 0) cout <<原创 2020-10-04 20:38:29 · 309 阅读 · 0 评论 -
单调队列
滑动窗口(题目链接)分析仅以最小值为例,根据样例进行分析 1 3 -1 -3 5 3 6 7以k为步长,在取值的过程中-1比-3先入队,所以有-3的地方一定会存在-1又因为每次均取得最小值,故-3在的情况下-1永远不可能作为答案输出所以,-3进队的时候可以将-1出队重复此过程即可形成单调递增队列,每次取队列头即可渠道每个窗口的最小值代码数组模拟队列,在此法中,队列中存储的是原数组的下标#include <bits/stdc++.h>using namespace std;t原创 2020-10-04 18:21:53 · 134 阅读 · 0 评论 -
单调栈
单调栈(题目连接)分析根据样例: 3 4 2 7 5若2存在,则前面的3 4永远不可能作为答案输出,所以在栈中删掉即可基于这种思想,即可在栈中得到单调上升的序列代码使用vis做栈#include <bits/stdc++.h>using namespace std;typedef long long ll;stack<int> vis;int main(){ int n; scanf("%d", &n); while (n--)原创 2020-10-04 15:00:48 · 342 阅读 · 0 评论 -
素数
试除法判断是否为素数#include <bits/stdc++.h>using namespace std;typedef long long ll;int n;int main(){ cin >> n; while (n--) { int m; cin >> m; if (m < 2) cout << "No" << endl;原创 2020-10-03 19:23:40 · 104 阅读 · 0 评论