算法模板
Wizard go
这个作者很懒,什么都没留下…
展开
-
和谐宿舍(贪心+二分)
和谐宿舍 我的某室友学过素描,墙上有n张他的作品。这些作品都是宽度为1,高度不定的矩形,从左到右排成一排,且底边在同一水平线上。 宿舍评比就要来了,为了及格,我们决定买不多于m块的矩形木板,把这些作品和谐掉。要求木板也从左到右排成一排,且底边与作品的底边在同一水平线上。 在能够把所有作品和谐掉的前提下,我们希望最大的那块木板的面积最小,问最大木板的面积。 输入格式 第一行两个数n和m,表示作品数和木板数; 第二行n个数Hi,表示从左到右第i个作品的高度。 输出格式 一行一个数ans,表原创 2022-04-04 18:49:05 · 860 阅读 · 0 评论 -
求乘法逆元
已知a*x≡1(mod m) ,求a的逆元 解法1:若m是质数,可用快速幂求解 int qmi(int a, int b, int p) { int res=1; while(b) { if(b&1) res=1ll*res*a%p; b>>=1; a=1ll*a*a%p; } return res; } //a的逆元:x=a^m-2 int x=qmi(a,m-2,m); 解法2:若m不是质数,只能用扩展欧几里得算法 //扩展欧几里得求逆元 int exgcd原创 2022-02-24 09:50:20 · 77 阅读 · 0 评论 -
19.堆排序
堆中根节点存储的是最小值,每一个节点的左右儿子都是大于或等于该节点。 插入元素: 将新元素插在最后,heap[++size]=x,再依次与其父节点比较,若该元素小于其父节点,则和父节点交换位置,循环直到正确位置。 删除元素: 用最后一个元素覆盖要删除的元素,并删除最后一个元素,heap[k]=heap[size–], 再通过down和up操作到正确位置。 修改元素: heap[k]=x,再执行down和up操作。 对于父节点x,其左儿子为2x,右儿子为2x+1;(从1开始) // h[N]存储堆中的值, h原创 2022-01-27 16:24:24 · 75 阅读 · 0 评论 -
18.并查集
基本操作: 1.将两个集合合并 2.查询两个集合是否在同一个集合中 基本原理:每个集合用一颗树来表示,树根的编号就是整个集合的编号,每个节点存储它的父节点,p[x]表示x的父节点。对于根结点,p[x]=x。 问题1:如何判断树根,看根节点x是否等于p[x] 问题2:如何查询元素所属的集合编号,从该元素向上搜索,直至找到根节点。 递归方式: int find(int x) { if(p[x]!=x) p[x]=find(p[x]); return p[x]; } 循环方式: int find原创 2022-01-26 13:33:44 · 181 阅读 · 0 评论 -
17.Tire树
维护一个字符串集合,支持两种操作: I x 向集合中插入一个字符串 x; Q x 询问一个字符串在集合中出现了多少次。 共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。 维护一个字符串集合,支持两种操作: I x 向集合中插入一个字符串 x; Q x 询问一个字符串在集合中出现了多少次。 共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。 输入格式 第一行包含整数 N,表示操作数。 接下来 N 行,每行包含一个操作指令,指令为 I x 或 Q x 中原创 2022-01-26 11:57:01 · 87 阅读 · 0 评论 -
17.单调栈和单调队列
单调栈 常见模型:找出每个数左边离它最近的比它大/小的数 int tt = 0; for (int i = 1; i <= n; i ++ ) { while (tt && check(stk[tt], i)) tt -- ; stk[ ++ tt] = i; } 单调队列 常见模型:找出滑动窗口中的最大值/最小值 int hh = 0, tt = -1; for (int i = 0; i < n; i ++ ) { while (hh <= t原创 2022-01-25 11:23:37 · 144 阅读 · 0 评论 -
16.栈和队列
栈 // tt表示栈顶 int stk[N], tt = 0; // 向栈顶插入一个数 stk[ ++ tt] = x; // 从栈顶弹出一个数 tt -- ; // 栈顶的值 stk[tt]; // 判断栈是否为空 if (tt > 0) { } 队列 //hh表示队头,tt表示队尾 int q[N],hh=0,tt=-1 //入队 q[++t]=x; //出队 hh++; //队头 q[hh]; //看队列是否为空 if(hh<=tt) { } 循环队列 // hh 表示队头,原创 2022-01-24 11:37:49 · 193 阅读 · 0 评论 -
15.数组模拟双链表
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点 int e[N], l[N], r[N], idx; // 初始化 void init() { //0是左端点,1是右端点 r[0] = 1, l[1] = 0; idx = 2; } // 在节点a的右边插入一个数x void insert(int a, int x) { e[idx] = x; l[idx] = a, r[idx] = r[a]; l原创 2022-01-24 11:26:30 · 237 阅读 · 0 评论 -
14.数组模拟单链表
//head表示头指针,e[N]表示当前节点的值,ne[N]表示当前节点的next指针,idx表示当前用到哪个节点 int head, e[N],ne[N],idx; void init() { head=-1; idx=0; } //在链表头插入一个元素 void insert(int a) { e[idx]=a; ne[idx]=head; head=idx++; } //删除头结点 void remove() { head=ne[head]; } ...原创 2022-01-24 11:01:12 · 64 阅读 · 0 评论 -
13.区间合并
1.按照区间左端点排序 2.区间有如下几种情况: (1)区间1包含区间2 (2)区间1和区间2有交集,即区间2的左端点<=区间1的右端点。 (3)区间1和区间2没有交集,即区间2的左端点>区间1的右端点。 typedef pair<int,int> PII; void merge(PII &segs) { sort(segs.begin(), segs.end());//排序 vector<PII> res;//用来存储合并后的区间 int st=-2e9,原创 2022-01-19 16:03:38 · 59 阅读 · 0 评论 -
12.离散化
对于一个值域很大的数组,如109,里面只包含稀疏的元素,我们没办法直接开辟一个一样大的数组来遍历对数组中的元素进行操作,这时我们就需要进行离散化处理。 比如对于一个109的数组,我们要操作的只有下面5个元素 我们可以用vector存取上述该元素的所有索引 [ 1,2,10,100,10000 ] 现在的数组空间就变成vector.size(); 接下来我们只需要用二分法对每一个索引进行定位,求出对应离散化的值 这样我们就将需要操作的元素映射到一个低维数组。 代码模板: vector<int>原创 2022-01-19 12:09:57 · 232 阅读 · 0 评论 -
11.位运算
求一个数n的二进制表示的第k位是多少 n>>k&1 求一个二进制数n中1的个数: n&-n = n&(~n+1) int lowbit(int n) { return n&-n;//返回最末尾的1 } int count=0; while(n) { n-=lowbit(n); count++; }原创 2022-01-19 10:33:56 · 44 阅读 · 0 评论 -
10.双指针
先使用暴力求解,再看i,j之间是否有联系进行优化 for (int i = 0, j = 0; i < n; i ++ ) { while (j < i && check(i, j)) j ++ ; // 具体问题的逻辑 } 练习题:最初不重复子序列原创 2022-01-19 09:49:38 · 172 阅读 · 0 评论 -
9.一维差分和二维差分
一维差分 an=b1+b2+·······+bn,a是前缀和数组,b是差分数组 bn=an-an-1 要在原数组某个区域[l,r]加上c,只需要 b[l]+=c; b[r+1]-=c; void insert(int l,int r,int c) { b[l]+=c; b[r+1]-=c } //前缀和 for(int i=1;i<=n;i++) b[i]+=b[i-1]; 二维差分 在(x1,y1),(x2,y2)的区域加上c, void insert(int x1,int y1,int x原创 2022-01-18 15:31:06 · 61 阅读 · 0 评论 -
8.二维前缀和
#include<iostream> const int N=1010; int n,m,q; int a[N][N],s[N][N]; int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",a[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++原创 2022-01-17 17:55:59 · 236 阅读 · 0 评论 -
6.高精度乘法I
A是大数,b是小数。方法与加分类似,用A的每一位乘b,t/10记录进位,t%10保留个位。 #include<iostream> #include<vector> using namespace std; vector<int> mul(vector<int> &A,int B) { vector<int> C; int t=0; for(int i=0;i<A.size() || t;i++) {原创 2022-01-17 15:22:19 · 71 阅读 · 0 评论 -
5.高精度减法
对于两个数A和B; 如果A<B;-(B-A) 如果A>=B;A-B 与加分相同,先倒序放到两个数组,对于每一个如果a>b,那么直接a-b,如果a<b;需要借位,a+10-b 判断A和B谁大 bool cmp(vector<int> &A,vector<int> &B) { if(A.size()!=B.size()) return A.size()-B.size(); for(int i=A.size()-1;i>=0;i--)原创 2022-01-16 11:19:25 · 98 阅读 · 0 评论 -
4.高精度加法
对于两个A和b,假设A是大数,b是小数,两者要想加,为了保证不溢出。 A:123456789 b:1234 把A和b分别倒序存放在数组中 A:987654321 b:4321 接下来我们只需要像人工执行加分一样,从第一位开始相加,进1就把它加到下一位 vector<int> add(vector<int> &A,vector<int> &B) { vector<int> C; int t=0; for(int i=0;i<A.siz原创 2022-01-16 10:08:27 · 93 阅读 · 0 评论 -
3.整数二分和浮点数二分
整数二分 1.先写一个check函数 2.判定在check的情况下(true和false的情况下),如何更新区间。 3.在check(m)==true的分支下是: l=mid的情况,中间点的更新方式是m=(l+r+1)/2 r=mid的情况,中间点的更新方式是m=(l+r)/2 int l=0,r=n-1; while(l<r) { int mid=l+r>>1; if(check(mid)) r=mid; else l=mid+1; } } int l=0,r=n-1; whi原创 2022-01-15 17:24:00 · 82 阅读 · 0 评论 -
2.归并排序
归并排序 1.确定分界点:mid=(l+r)/2 2.递归排序left,right 3.归并合二为一 int tmp[N];//辅助数组存元素 void merge_sort(int q[],int l,int r) { if(l>=r) return; int mid=l+r>>1;//确定中点为分界点 merge_sort(q,l,mid),merge_sort(q,mid+1,r); int k=0,i=l,j=mid+1;//i,j分别指向左半段和右半段的开头 while原创 2022-01-15 10:50:45 · 144 阅读 · 0 评论 -
1.快速排序
快速排序 1.确定分界点x 2.调整元素使得分界点左边的元素小于x,右边的元素大于x。此时就分成了两段。 3.递归左右两段 算法模板 void quick_sort(int q[],int l,int r) { if(l>=r) return; int x=q[l+r>>1];//确定分界点 int i=l-1,j=r+1;//从两侧出发,执行时左右落到的就都是头尾元素 while(l<r) { do i++; while(q[i]<x); do j--; w原创 2022-01-15 09:58:22 · 100 阅读 · 0 评论