快速排序
模板题:785. 快速排序 - AcWing题库
思路:
定义一个x(一般喜欢用中间的),我们快速排序,让x左边的都比它小,同时让右边的都比它大。然后像二分一样不断细分,缩小范围进行同样的操作。
细节:让x左边的都比它小,同时让右边的都比它大。双指针算法,只要满足条件就移动指针,否则就交换两个数。
像二分一样不断细分,缩小范围进行同样的操作。递归操作。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,q[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return;
int i=l-1,j=r+1,x=q[l+r>>1];//这里多一位的原因是下面的do-while
while(i<j)
{
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j) swap(q[i],q[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>q[i];
}
quick_sort(q,1,n);
for(int i=1;i<=n;i++)
{
cout<<q[i]<<" ";
}
return 0;
}
归并排序
模板题:787. 归并排序 - AcWing题库
思路:确定分界点,递归排序,归并--合二为一。
两段排好序后,用两个指针分别指向两端中最小的数。比较两个指针的大小,输出小的,移动指针。直到一段被输出完,直接按顺序输出另一段即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,q[N],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=1,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<=q[j]) tmp[k++]=q[i++];
else tmp[k++]=q[j++];
}
//退出了就说明,i>mid或j>r,就需要把剩下的数直接放进数组里
while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
for(i=l,j=1;i<=r;i++,j++)
{
q[i]=tmp[j];
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i];
merge_sort(q,1,n);
for(int i=1;i<=n;i++) cout<<q[i]<<" ";
return 0;
}
逆序对的数量
788. 逆序对的数量 - AcWing题库
看题解:AcWing 788. 逆序对的数量--图解 - AcWing
(这篇题解里很多大佬欸~看完评论才懂)
计算逆序对的数量,我们的思路是在归并过程中找出逆序对数量。
每次归并所使用的上次归并的结果数组都是有序的,所以如果[l,mid] 之间如果有q[i]大于右边数组q[j]的值,则[i,mid]之间的所有值都是大于q[j]的,可以用mid-i+1来计算
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,q[N],tmp[N];
long long res;
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=1,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else
{
res += mid - i + 1;
tmp[k ++ ] = q[j ++ ];
}
}
while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
for(i=l,j=1;i<=r;i++,j++)
{
q[i]=tmp[j];
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i];
merge_sort(q,1,n);
cout<<res;
return 0;
}
二分
整数二分
789. 数的范围 - AcWing题库
注意这个板子在if里面最后给l或r赋值,必须要+1或者-1。用res记录时不用担心漏掉,不用res记录的情况正好需要-1/+1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,q,a[N];
int sl(int x)//左边界
{
int l=0,r=n-1,res=-1;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]>=x)
{
res=mid;
r=mid-1;
}else l=mid+1;
}
if(a[res]==x) return res;
else return -1;
}
int sr(int x)
{
int l=0,r=n-1,res=-1;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]<=x)
{
res=mid;
l=mid+1;
}else r=mid-1;
}
if(a[res]==x) return res;
else return -1;
}
signed main()
{
cin>>n>>q;
for(int i=0;i<n;i++) cin>>a[i];
while(q--)
{
int k;
cin>>k;
cout<<sl(k)<<" "<<sr(k)<<endl;
}
return 0;
}
个人喜欢用的板子:整数二分
int l=1,r=n,res=-1;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid))
{
res=mid;
l=mid+1;
}else r=mid-1;
}
实数二分
790. 数的三次方根 - AcWing题库
纯板子题
#include<bits/stdc++.h>
using namespace std;
double n;
signed main()
{
cin>>n;
double l=-1e4,r=1e4;
for(int i=1;i<=100;i++)//二分100次
{
double mid=(l+r)/2;
if(pow(mid,3)<=n)
{
l=mid;
}else r=mid;
}
printf("%.6f",l);
return 0;
}
放一个实数二分的模板:
double l=-1e4,r=1e4;//这里是数的范围
for(int i=1;i<=100;i++)//二分100次,100次就够了很精细了
{
double mid=(l+r)/2;
if(check(mid))
{
l=mid;
}else r=mid;
}
//最后输出l或者r都行,因为我们已经很精细了,这俩值最后是相等的
高精度
加法
791. 高精度加法 - AcWing题库
模拟题,可以参考之前写的一篇博客。
千万要记得字符数组到整型数组的转换!!
【算法】模拟,高精度_想七想八不如11408的博客-CSDN博客
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
string A,B;
int a[N],b[N],c[N];
signed main()
{
cin>>A>>B;
for(int i=A.length()-1,j=1;i>=0;i--,j++)
{
a[j]=A[i]-'0';
}
for(int i=B.length()-1,j=1;i>=0;i--,j++)
{
b[j]=B[i]-'0';
}
int len=max(A.length(),B.length());
for(int i=1;i<=len;i++)
{
c[i]+=a[i]+b[i];
c[i+1]=c[i]/10;
c[i]%=10;
}
if(c[len+1]) len++;
for(int i=len;i>=1;--i) cout<<c[i];
return 0;
}
减法
792. 高精度减法 - AcWing题库
是一个模拟题。
我们主要写的是大数减小数的情况,因此前面要讨论:
如果完全一样,直接返回0。
再对长度进行讨论,主要是长度相同的情况。从高位到低位进行比较,如果a大,我们就可以放心了。如果相等就继续下一位讨论,最后一种情况就是a小了,直接交换。
减法就是加法的一种特殊形式,加上一个负数嘛。但是要注意加法是进位,相应的减法是减位。
最后也要记得去除前导0。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
string A,B;
int a[N],b[N],c[N];
int flag;//标记A,B是否交换过,也就代表是否有负号
signed main()
{
cin>>A>>B;
if(A.compare(B)==0)
{
cout<<"0";
return 0;
}
if(B.length()>A.length())
{
swap(A,B);
flag=1;
}else if(B.length()==A.length()){//长度相同
for(int i=0;i<A.length();i++)
{
if((A[i]-'0')>(B[i]-'0'))//如果a大直接就结束循环了
{
break;
}else if((A[i]-'0')==(B[i]-'0')){
continue;
}else{
swap(A,B);
flag=1;
}
}
}
for(int i=A.length()-1,j=1;i>=0;i--,j++)
{
a[j]=A[i]-'0';
}
for(int i=B.length()-1,j=1;i>=0;i--,j++)
{
b[j]=B[i]-'0';
b[j]=-b[j];
}
int len=max(A.length(),B.length());
for(int i=1;i<=len;i++)
{
c[i]+=a[i]+b[i];
if(c[i]<0)
{
a[i+1]--;
c[i]+=10;
}
}
while(!c[len]) len--;
if(flag) cout<<"-";
for(int i=len;i>=1;i--) cout<<c[i];
return 0;
}
乘法
793. 高精度乘法 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N],c[N];
signed main()
{
string A,B;
cin>>A>>B;
int lena=A.length(),lenb=B.length();
int len=lena+lenb;
for(int i=lena-1,j=1;i>=0;j++,i--)
{
a[j]=A[i]-'0';
}
for(int i=lenb-1,j=1;i>=0;j++,i--)
{
b[j]=B[i]-'0';
}
for(int i=1;i<=lena;i++)
{
for(int j=1;j<=lenb;j++)
{
c[i+j-1]+=a[i]*b[j];
}
}
for(int i=1;i<=len;i++)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
while(!c[len]) len--;
for(int i=max(1,len);i>=1;i--) cout<<c[i];
//注意0,0的情况 ,要输出一个0
return 0;
}
除法
794. 高精度除法 - AcWing题库
模板是大数(高精度)除以小数
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int b,a[N],c[N];
int main()
{
string A;
cin>>A>>b;
for(int i=0;i<A.length();i++)
{
a[i]=A[i]-'0';
}
int r=0;
for(int i=0;i<A.length();i++)
{
r=r*10+a[i];
c[i]=r/b;
r%=b;
}
int k;
for(int i=0;i<A.length();i++)
{
if(!c[i]) continue;
else{
k=i;
break;
}
}
if(k==0&&c[0]==0)//说明都是0
{
cout<<"0";
}else{
for(int i=k;i<A.length();i++) cout<<c[i];
}
cout << endl << r << endl;
return 0;
}
前缀和
【蓝桥杯】二分、前缀和、差分练习_想七想八不如11408的博客-CSDN博客
795. 前缀和 - AcWing题库
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],s[N];
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
while(m--)
{
int l,r;
cin>>l>>r;
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
二维前缀和
796. 子矩阵的和 - AcWing题库
注意画图
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int a[N][N],s[N][N];
signed main()
{
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
}
while(q--)
{
int x,y,xx,yy;
cin>>x>>y>>xx>>yy;
cout<<s[xx][yy]+s[x-1][y-1]-s[x-1][yy]-s[xx][y-1]<<endl;
}
return 0;
}
差分
797. 差分 - AcWing题库
题解:AcWing 797. 算法基础班--第一章--11.一维差分模板 - AcWing
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N],m;
int b[N];//差分数组
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-a[i-1];//构建差分数组
//这样的话,对b求前缀和就能得到a
}
while(m--)
{
int l,r,c;
b[l]+=c,b[r+1]-=c; //进行操作
}
for(int i=1;i<=n;i++)
{
a[i]=b[i]+a[i-1];//an = b1 + b2 + b3 + … + bn
cout<<a[i]<<" ";
}
return 0;
}
二维差分
AcWing 798. 差分矩阵 - AcWing
#include<bits/stdc++.h>
using namespace std;
const int N =1010;
int n,m,q;
int a[N][N],b[N][N];
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1];
}
}
while(q--)
{
int x,y,xx,yy,c;
cin>>x>>y>>xx>>yy>>c;
b[x][y]+=c;
b[xx+1][y]-=c;
b[x][yy+1]-=c;
b[xx+1][yy+1]+=c;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cout<<b[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
双指针
最长连续不重复子序列
799. 最长连续不重复子序列 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int n;
int a[N],s[N];
int ans;//注意看题,是最长连续不重复的序列
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,j=1;i<=n;i++)
{
s[a[i]]++;
while(s[a[i]]>1)//必须要注意这里是while,要把j移到能移到的最右边
{
s[a[j]]--;
j++;
}
ans=max(ans,i-j+1);
}
cout<<ans;
return 0;
}
数组元素的目标和
800. 数组元素的目标和 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int n,m,x;
int a[N],b[N];
signed main()
{
cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
int i=0,j=m-1;
while(i<n&&j<m)
{
if(a[i]+b[j]>x)
{
j--;
}else if(a[i]+b[j]<x){
i++;
}else{
cout<<i<<" "<<j;
return 0;
}
}
return 0;
}
判断子序列
2816. 判断子序列 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int n,m;
int a[N],b[N];
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
if(n>m)
{
cout<<"No"<<endl;
return 0;
}
int i=1,j=1;
while(i<=n&&j<=m)
{
if(a[i]==b[j])
{
if(i==n)
{
cout<<"Yes"<<endl;
return 0;
}
j++;
i++;
}else{
j++;
}
}
cout<<"No"<<endl;
return 0;
}
位运算
把第k位移到最后一位 n>>k
看个位是几 x&1
举个栗子:
#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int n,m;
int a[N],b[N];
signed main()
{
int n=10;//10的二进制表示是1010
for(int k=3;k>=0;k--)
{
cout<<(n>>k&1)<<" ";
}
return 0;
}
lowbit操作(是树状数组的基本操作),是返回x的最后一位1
x=1010 lowbit(x)=10
x=101000 lowbit(x)=1000
lowbit(x)其实就是x&-x,
-x是原数的补码,是取反加1。-x=~x+1
x =101 0000 1000
~x =010 1111 0111
~x+1 = 010 1111 1000
x&-x=x&(~x+1) = 000 0000 1000
801. 二进制中1的个数 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int lowbit(int x)
{
return x&-x;
}
signed main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
int res=0;
while(x) x-=lowbit(x),res++;
cout<<res<<" ";
}
return 0;
}
离散化
802. 区间和 - AcWing题库
“假定有一个无限长的数轴”,需要离散化。特点跨度很大,但非常稀疏。
#include<bits/stdc++.h>
using namespace std;
const int N =300010;
int n,m,a[N],s[N];
typedef pair<int,int> PII;
vector<int> alls;//存所有待离散化的坐标
vector<PII> add,query;//存操作
int find(int x)
{
int l=0,r=alls.size()-1,res=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(alls[mid]>=x) r=mid-1,res=mid;
else l=mid+1;
}
return res+1;//映射成从1开始
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x,c;
cin>>x>>c;
add.push_back({x,c});
alls.push_back(x);
}
while(m--)
{
int l,r;
cin>>l>>r;
query.push_back({l,r});
alls.push_back(l);
alls.push_back(r);
}
//去除
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
for(auto item:add)
{
int x=find(item.first);
a[x]+=item.second;
}
for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];//处理前缀和
for(auto item:query)
{
int l=find(item.first),r=find(item.second);
cout<<s[r]-s[l-1]<<endl;//前缀和
}
return 0;
}
区间合并
803. 区间合并 - AcWing题库
区间合并指的是合并两个有交集的区间。按区间左端点排序
#include<bits/stdc++.h>
using namespace std;
const int N =100010;
typedef pair<int,int> PII;
int n;
vector<PII> segs;
void merge(vector<PII> ®s)
{
vector<PII> res;
sort(segs.begin(),segs.end());
int st=-2e9,ed=-2e9;
for(auto seg:segs)
{
if(ed<seg.first)
{
if(st!=-2e9) res.push_back({st,ed});
st=seg.first,ed=seg.second;
}else ed=max(ed,seg.second);
}
if(st!=-2e9) res.push_back({st,ed});
segs=res;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int l,r;
cin>>l>>r;
segs.push_back({l,r});
}
merge(segs);
cout<<segs.size()<<endl;
return 0;
}
第一章完结撒花,略略略~