第一章 基础算法

排序

快速排序

int 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];
    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);
}

归并排序

int q[N];
int temp[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;	
    while(i<=mid&&j<=r){
        if(q[i]<=q[j])temp[k++]=q[i++];
        else temp[k++]=q[j++];
    }
    //归并
    while(i<=mid)temp[k++]=q[i++];
    while(j<=r)temp[k++]=q[j++];
    
    for(i=l,j=0;i<=r;i++,j++)q[i]=temp[j];
}
//调用,数据从0开始储存。
merge_sort(q,0,n-1);

典题

【归并排序求逆序对】

​ 同时逆序对还可以用树状数组的方式求。时间复杂度同样是 O ( n l o g n ) O(nlogn) O(nlogn)

//只要在while里边加一行就行了
while(i<=mid&&j<=r){
    if(a[i]<=a[j])b[k++]=a[i++];
    else{
        ans+=mid-i+1;
        b[k++]=a[j++];
    }
}

二分

整数二分

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:求左边界。
int bsearch_1(int l,int r){
    while(l<r){
        int mid=l+r>>1;
        if(check(mid))r=mid;//check()判断mid是否满足性质
        else l=mid+1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:求右边界。
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;
}

实数二分

//固定循环次数
for(int i=0;i<100;i++){//求mid
    double mid=(l+r)/2;
    if(calc(mid))r=mid;
    else l=mid;
}
//eps精度
const double eps=1e-8;//一般将精度定为要求的后两位。
double l=-10000,r=10000;
while(r-l>eps){
    double mid=(l+r)/2;
    if(check(mid))r=mid;
    else l=mid;
}

典题

【连续边界】

​ 有序数组中找某一数字的最大连续连续边界。

//找左边界
while(l<r){
    int mid=l+r>>1;
    if(a[mid]>=t)r=mid;
    else l=mid+1;
}
//找右边界
while(l<r){
    int mid=l+r+1>>1;
    if(a[mid]<=t)l=mid;
    else r=mid-1;
}

【n次方根】

​ 使用实数二分实现,例求三次方根。

double l=-10000,r=10000;
while(r-l>1e-8){
    double mid=(l+r)/2;
    if(mid*mid*mid>=n)r=mid;
    else l=mid;
}

高精度

​ 将字符串转化为倒序数组。

for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');

高精度加法

//C=A+B
vector<int> add(vector<int> &a,vector<int> &b){
    vector<int>c;
    int t=0;
    for(int i=0;i<a.size()||i<b.size();i++){
        if(i<a.size())t+=a[i];
        if(i<b.size())t+=b[i];
        c.push_back(t%10);
        t/=10;
    }
    if(t)c.push_back(1);
    return c;
}

高精度减法

//判断a和b的大小关系,直接用string的比较会出问题
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--)
        if(A[i]!=B[i])
            return A[i]>B[i];
    return true;
}
//C=A-B
vector<int> sub(vector<int> &A,vector<int> &B){
    vector<int> C;
    for(int i=0,t=0;i<A.size();i++){
        t=A[i]-t;
        if(i<B.size())t-=B[i];
        C.push_back((t+10)%10);
        if(t<0)t=1;
        else t=0;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();//去除前导0
    return C;
}

高精度乘法

//C=A*B(B是小整数)
vector<int> mul(vector<int> &A,int b){
    vector<int> C;
    int t=0;
    for (int i=0;i<A.size()||t;i++){
        if(i<A.size())t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();//去除前导0
    return C;
}

高精度除法

//C=A/B(B为小整数);可求余数
vector<int> div(vector<int> &A,int b){//r是引用传递
    vector<int> C;//商
    int r=0;//余数
    for (int i=A.size()-1;i>=0;i--){//从最高位开始算
        r=r*10+A[i];
        C.push_back(r/b);
        r%=b;
    }
    reverse(C.begin(),C.end());
    while(C.size()>1&&C.back()==0)C.pop_back();//去除前导0
    return C;
}

前缀和

​ 前缀和多用于频繁询问区间和的问题。

一维前缀和

//创建
s[i]=s[i-1]+a[i];
//查找
a[l~r]=s[r]-s[l-1];

二维前缀和

//创建
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i];
//查找
s[x1~x2][y1~y2]=s[x2][y2]+s[x1-1][y1-1]-s[x1-1][ y2]-s[x2][y1-1];

三维前缀和

//创建
s[i][j][k]=s[i-1][j][k]+s[i][j-1][k]+s[i][j][k-1]+t
			-s[i-1][j-1][k]-s[i-1][j][k-1]-s[i][j-1][k-1]
			+s[i-1][j-1][k-1];
//查找
s[x1~x2][y1~y2][z1~z2]=s[x2][y2][z2]
						-s[x2][y1][z2]-s[x1][y2][z2]-s[x2][y2][z1]
						+s[x1][y1][z2]+s[x1][y2][z1]+s[x2][y1][z1]
						-s[x1][y1][z1];

典题

【矩形包含】

​ 给定 n n n 个矩形的长和宽,每次询问两个矩形的长宽,一小一大,求长宽的范围都在这两个矩形中的矩形数量。(重叠不算,不能旋转)

//使用二维前缀和统计,数组的坐标表示长和宽,记录一个矩形相当与在g[i][j]++,然后处理1000*1000的前缀和。

差分

​ 差分多处理频繁对区间进行整体加减的问题。

一维差分

​ 对序列 [ l , r ] [l,r] [l,r] 加上 c c c

//构造一维差分
void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}
for(int i=1;i<=n;i++){
    scanf("%d",&a[i]);
    insert(i,i,a[i]);
}
//调用操作
while(m--){
    int l,r,c;
    scanf("%d%d%d",&l,&r,&c);
    insert(l,r,c);
}
//前缀和
for(int i=1;i<=n;i++)b[i]+=b[i-1];

二维差分

//构造二维差分
void insert(int x1,int y1,int x2,int y2,int c){
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        scanf("%d",&a[i][j]);
        insert(i,j,i,j,a[i][j]);
    }
//调用操作
while(q--){
    int x1,x2,y1,y2,c;
    scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
    insert(x1,y1,x2,y2,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];

双指针

典题

最长不重复子序列(判断是否子序列)

for(int i=0,j=0;i<n;i++){
    s[a[i]]++;
    while(j<i&&s[a[i]]>1)s[a[j++]]--;
    res=max(res,i-j+1);
}

求两个数组中相加等于 x 的数量(数对)

for(int i=0,j=m-1;i<n;i++){
    while(j>=0&&a[i]+b[j]>x)j--;
    if(j>=0&&a[i]+b[j]==x)cout<<i<<" "<<j<<endl;
}

位运算

快速幂

while(tmp){
    if(tmp&1)sum++;
    tmp>>=1;
}

典题

【a 的 b 次幂 mod p】

long long res=1;
while(b){
    if(b&1)res=res*a%p;
    a=a*a%p;
    b>>=1;
}

【a 乘 b mod p】

​ 用高精度不好求模。

long long res=0;
while(b){
    if(b&1)res=(res+a)%p;
    a=(a+a)%p;
    b>>=1;
}

【第 k 位二进制】

if(x>>k&1)puts("1");
else puts("0");

【最后一位 1】

int lowbit(int x){return x&(-x);}

离散化

​ 在线离散化,不用存下来,直接使用 map。

unordered_map<int,int>mp;
int get(int x){
    if(!mp.count(x))mp[x]=++cnt;
    return mp[x];
}

​ 离线离散化,需要将数据存下来,并进行排序判重。

//去重(去重是一种双指针算法)
vector<int> alls;//存储所有待离散化的值
sort(alls.begin(),alls.end());//将所有的值排序
alls.erase(unique(alls.begin(),alls.end()),alls.end());//去掉重复元素

int find(int x){//找到第一个大于等于x的位置
	int l=0,r=alls.size()-1;
    while(l<r){
		int mid=l+r>>1;
        if(alls[mid]>=x)r=mid;
        else l=mid+1;
    }
    return r+1;//映射到1,2,...(不加1从0开始)
}

区间合并

​ 更像贪心。

【区间选点】

​ 将每个区间按右端点从小到大排序。
二、从前往后依次枚举每个区间

  • ①如果当前区间中已经包含点,则直接pass
  • ②否则选择当前区间的右端点。

【区间合并】

​ 这是一个贪心题,①按照区间左端点排序。②遍历。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值