蓝桥杯算法模板
2024/4/5:happy:🐉🐲 📓by Michael Zhao 🌉必胜
快速幂
typedef long long LL
LL qmi(LL a,LL n,LL q){
LL res=1%q;
while(b){
if(n&1) res=res*a%q;
a=a*a%q;
n=n>>1;
}
return res;
}
GCD
int gcd(int a,int b)
{
if(b==0) return a;
gcd(b,a%b);
}
LCM
int lcm(int a,int b){
return a/gcd(a,b)*b;//除去公因数再乘以倍数
}
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
全排列
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(),nums.end());
do{
ret.emplace_back(nums);
}while(next_permutation(nums.begin(),nums.end()));
return ret;
}
并查集
int n,m,cnt=0;
int f[10005];
int find(int x){
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
void union(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
f[a]=b;
cnt++;
}
}
背包
//01背包
#include <iostream>
using namespace std;
int main(){
int v[MAXN],w[MAXN],dp[MAXN];
int m,n;//m为总背包大小,n为物品个数
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m];
}
//完全背包
#include <iostream>
using namespace std;
int main(){
int v[MAXN],w[MAXN],dp[MAXN];
int m,n;//m为总背包大小,n为物品个数
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=v[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m];
}
LCS——最长公共子序列
/*
|求最长公共子序列|
|递推形式|
*/
void solve() {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (s1[i] == s2[j]) {
dp[i + 1][j + 1] = dp[i][j] + 1;
}else {
dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
} } }
}
树状数组
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50005;
int a[maxn];
int n;
int lowbit(const int t) {
return t & (-t);
}
void insert(int t, int d) {
while (t <= n){
a[t] += d;
t = t + lowbit(t);
}
}
ll getSum(int t) {
ll sum = 0;
while (t > 0){
sum += a[t];
t = t - lowbit(t);
}
return sum;
}
润年判断
bool isleaf(int x){
return x%400==0||(x%4==0&&x%100!=0);
}
筛选素数
bool isprime(int n){
if(n==1)return false;
for(int i=2;i*i<n;i++){
if(n%i==0)return false;
}
return true;
}
前缀和
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,a[N],b[N];
int main() {
ios::sync_with_stdio(false);
cin>>n>>m;
for (int i=1; i<=n; i++) {
cin>>a[i];
b[i]=a[i]+b[i-1];
}
for (int i=1; i<=m; i++) {
int x,y;
cin>>x>>y;
cout<<b[y]-b[x-1]<<endl;
}
return 0;
}
差分
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N],b[N];
int n,m,l,r,c,x;
int main() {
cin>>n>>m;
for (int i=1; i<=n; i++) {
cin>>a[i];
b[i]=a[i]-a[i-1];
}
while (m--) {
cin>>l>>r>>c;
b[l]+=c;
b[r+1]-=c;
}
for (int i=1; i<=n; i++) {
x+=b[i];
cout<<x<<" ";
}
return 0;
}
二分查找
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],x,ans;
int main() {
ios::sync_with_stdio(0);
cin>>n;
for (int i=1; i<=n; i++) {
cin>>a[i];
}
cin>>x;
int l=1,r=n;
while (l<=r) {
int mid=(l+r)>>1;
if (a[mid]==x) {
ans=mid,r=mid-1;
} else if (a[mid]<x) {
l=mid+1;
} else {
r=mid-1;
}
}
if (!ans) {
cout<<-1;
} else {
cout<<ans;
}
return 0;
}
区间修改,区间查询
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,a[N],b[N],c[N],maxn;
int lowbit(int a) {
return a&(-a);
}
void update(int x,int y) {
for (int i=x; i<=n; i+=lowbit(i)) {
b[i]+=y;
c[i]+=x*y;
}
}
void range_update(int l,int r,int x) {
update(l,x);
update(r+1,-x);
}
int query(int x) {
int ans=0;
for (int i=x; i; i-=lowbit(i)) {
ans+=(x+1)*b[i]-c[i];
}
return ans;
}
int range_query(int l,int r) {
return query(r)-query(l-1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for (int i=1; i<=n; i++) {
cin>>a[i];
update(i,a[i]-a[i-1]);
}
for (int i=1; i<=m; i++) {
int q,x,y,k;
cin>>q;
if (q==1) {
cin>>x>>y>>k;
range_update(x,y,k);
} else {
cin>>x>>y;
cout<<range_query(x,y)<<endl;
}
}
return 0;
}
矩阵乘法
struct matrix {
int mat[N][N];
matrix() {
memset(mat,0,sizeof mat);
}
matrix operator*(const matrix& b)const {
matrix ans;
for (int i=1; i<=N; i++) {
for (int j=1; j<=N; j++) {
for (int k=1; k<=N; k++) {
ans.mat[i][j]=(ans.mat[i][j]+mat[i][k]*b.mat[k][j]%MOD)%MOD;
}
}
}
return ans;
}
};
KMP
vector<int> getNext(string s){
int n=s.size();
vector<int> v(n);
int i=0,j=-1;
while(i<n){
if(j==-1||s[i]==s[j]){
i++;
j++;
v[i]=j;
}else{
j=v[j];
}
}
}
int kmp(string s1,string s2){
vector<int> next=getNext(s2);
int i=0,j=0;
while(i<s1.size()&&j<s2.size()){
if(j==-1||s1[i]==s2[j]){
i++;
j++;
}else{
j=next[j];
}
}
if(j==s2.size()) return i-j;
else return -1;
}
SPFA
const int inf=1e9;
const int maxn=;
vector<int> to[maxn],weight[maxn];
int dis[maxn];
bool vis[maxn];
int n,start,dest;
void spfa(int start){
int i,j;
for(i=0;i<n;i++){
dis[i]=inf;
vis[i]=false;
}
dis[start]=0;
vis[start]=true;
queue<int> Q;
Q.push(start);
while(!Q.empty()){
int u=Q.front();
Q.pop();
vis[u]=false;
for(i=0;i<to[u].size();i++){
int v=to[u][i],w=weight[u][i];
if(dis[u]+w<dis[v]){
dis[v]=dis[u]+w;
if(!vis[v]){
vis[v]=true;
Q.push(v);
}
}
}
}
}
Vector
size()
empty()
clear()
begin()/end()
lower_bound/upper_bound
这两个二分查找操作可以在set,数组,vector,map中使用;
数组 或者 vector 中的语法:
序列是升序的(从小到大)
lower_bound(begin(),end(),x) //返回序列中第一个大于等于x的元素的地址
upper_bound(begin(),end(),x) //返回序列中第一个大于x的元素的地址
序列是降序的(从大到小)
lower_bound(begin(),end(),x,greater<tpye>()) //返回序列中第一个小于等于x的元素的地址
upper_bound(begin(),end(),x,greater<type>()) //返回序列中第一个小于x的元素的地址
set 或者 map 中的语法:
和数组差不多,只不过返回的是迭代器:
s.lower_bound(x) //返回序列中第一个大于等于x的元素的地址
s.upper_bound(x) //返回序列中第一个大于x的元素的地址
重点注意:如果当前序列中找不到符合条件的元素,那么返回end(),对于数组来说,返回查询区间的尾地址位置,对于set来讲,返回end()-1后面元素的迭代器,也就是end();
set
set/multiset
前者去重后者不去重
intsert() 插入一个数
find() 查找一个数
count() 返回一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
map
map/multimap
(它们都是关联容器,增删效率为log级别,并且依据key能自动排序,默认小于,前者key不允许重复,后者允许)
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()
string
string 是一个很强大的字符串类
size()/length() 返回字符串的长度
reverse(s.begin(),s.end()); 将字符串的反转
s.append(str) 在字符串后面加上字符串str
支持对两个字符串的 ’ + ‘ 操作,实现字符串的拼接(s.append(str)比 + 要慢)
s.erase(0,s.find_first_not_of('0')); //利用string函数去除前导0
s1=s2.substr(起始下标,拷贝长度) //string的截取
pos = s.find('x') //返回string里面字符x的下标;
字符串转化为数字的库函数:
string str = "16";
int a = atoi(str.c_str());//能转换整数和小数,能报异常
int b = strtol(str.c_str(), nullptr, 10);//能指定进制
数字转化为字符串的函数:
int val=123456;
string s=to_string(val);
iterator erase(iterator p):删除字符串中p所指的字符
iterator erase(iterator first, iterator last):删除字符串中迭代器区间 [first, last) 上所有字符
string& erase(size_t pos, size_t len):删除字符串中从索引位置 pos 开始的 len 个字符
Iterator
vector<type>::iterator iter;
map<type,type>::iterator iter;
set<type>::iterator iter;
等等.....
迭代器可以像指针一样,遍历STL时可以直接对迭代器 ++ -- ;
访问迭代器的值的形式:
*iter
iter->first iter->second
双指针
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
倍增LCA
void bfs(int root)//从根节点开始处理每个点的深度并且进行fa的倍增
{
memset(depth,0x3f,sizeof depth);
queue<int> q;
q.push(root);
depth[0]=0,depth[root]=1;//设置哨兵
while(q.size())
{
int t=q.front();
q.pop();
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(depth[j]>depth[t]+1)
{
depth[j]=depth[t]+1;
fa[j][0]=t;
q.push(j);
for(int k=1;k<16;k++)//倍增里面的常数根据题目节点个数而定
fa[j][k]=fa[fa[j][k-1]][k-1];
}
}
}
}
int lca(int a,int b)
{
if(depth[a]<depth[b]) swap(a,b);//对深度大的点进行往上跳跃
for(int k=15;k>=0;k--)
if(depth[fa[a][k]]>=depth[b])
a=fa[a][k];
if(a==b) return a;
for(int k=15;k>=0;k--)//两个点同时跳跃,直到最近公共祖先的下面的节点
{
if(fa[a][k]!=fa[b][k])
{
a=fa[a][k];
b=fa[b][k];
}
}
return fa[a][0];//返回a的父亲,也就是最近公共祖先
}
匈牙利算法
nt n1, n2; // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N], e[M], ne[M], idx; // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N]; // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N]; // 表示第二个集合中的每个点是否已经被遍历过
bool find(int x)
{
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true;
if (match[j] == 0 || find(match[j]))
{
match[j] = x;
return true;
}
}
}
return false;
}
// 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点
int res = 0;
for (int i = 1; i <= n1; i ++ )
{
memset(st, false, sizeof st);
if (find(i)) res ++ ;
}
字符串哈希
核心思想:将字符串看成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];
}
DP 最长公共子序列
int main()
{
int n,m;
cin >>n>>m;
for(int i=1;i<=n;i++) cin >>a[i];
for(int i=1;i<=m;i++) cin >>b[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
cout <<f[n][m]<<endl;
}