刷题打卡
快速排序
1.第k个数
第k个数
Description
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少。
Input
一行包含两个整数 n 和 k。
第二行包含 n 个整数(所有整数均在1~10^9范围内),表示整数数列。
1≤n≤100000,
1≤k≤n
Output
输出一个整数,表示数列的第k小数。
Sample Input 1
5 3
2 4 1 5 3
Sample Output 1
3
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
ll n,k;
ll a[100010];
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
cout<<a[k-1];
return 0;
}
归并排序
2.逆序对的数量
逆序对的数量
Description
给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。
Input
第一行包含整数n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
Output
输出一个整数,表示逆序对的个数。
Sample Input 1
6
2 3 4 5 6 1
Sample Output 1
5
Hint
1≤n≤100000
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
int n,k;
int a[100010],tmp[100010];
ll ans;
void msort(int l,int r)
{
if(l>=r) return;
int mid=(l+r)>>1;
msort(l,mid);
msort(mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r){
if(a[i]<=a[j]) tmp[k++]=a[i++];
else{
tmp[k++]=a[j++];
ans+=mid-i+1;
}
}
while(i<=mid) tmp[k++]=a[i++];
while(j<=r) tmp[k++]=a[j++];
for(i=l,j=0;i<=r;i++,j++) a[i]=tmp[j];
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
msort(1,n);
cout<<ans;
return 0;
}
二分
3.数的范围
Description
给定一个按照升序排列的长度为n的整数数组,以及q个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
Input
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
1≤n≤100000
1≤q≤10000
1≤k≤10000
Output
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
Sample Input 1
6 3
1 2 2 3 3 4
3
4
5
Sample Output 1
3 4
5 5
-1 -1
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
typedef long long ll;
const int N=100010;
int n,q,k,b[N];
map<int,int> ma;
int main()
{
cin>>n>>q;
for(int i=0;i<n;i++){
int x;
cin>>x;
b[x]++;
if(b[x]<=1) ma[x]=i;
}
while(q--){
cin>>k;
if(b[k]==0) cout<<"-1 -1\n";
else cout<<ma[k]<<" "<<ma[k]+b[k]-1<<endl;
}
return 0;
}
4.数的三次方根
Description
给定一个浮点数n,求它的三次方根。
Input
共一行,包含一个浮点数n。
−10000≤n≤10000
Output
共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
Sample Input 1
1000.00
Sample Output 1
10.000000
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
double x,y;
int main()
{
cin>>x;
printf("%.6lf",cbrt(x));
return 0;
}
高精度
5.高精度加法
Description
给定两个正整数,计算它们的和。
Input
共两行,每行包含一个整数。
1≤整数长度≤100000
Output
共一行,包含所求的和。
Sample Input 1
12
23
Sample Output 1
35
Hint
注意去除前导0
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
using namespace std;
const int N=100010;
char a1[N],b1[N];
int a[N],b[N],c[N];
int la,lb;
int main()
{
cin>>a1>>b1;
la=strlen(a1);
lb=strlen(b1);
for(int i=0;i<la;i++) a[la-i]=a1[i]-'0';
for(int i=0;i<lb;i++) b[lb-i]=b1[i]-'0';
int i,l,x=0;
for(i=1;i<=la+lb;i++){
c[i]=(a[i]+b[i]+x)%10;
x=(a[i]+b[i]+x)/10;
}
l=la+lb;
while(c[l]==0&&l>1) l--;
for(i=l;i>=1;i--) cout<<c[i];
return 0;
}
拓展:
高精度减法
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
using namespace std;
char a1[100010],b1[100010],c1[100010];
int a[100010],b[100010],c[100010];
int main()
{
int i,x,y,m=0,z=0;
scanf("%s",a1);
scanf("%s",b1);
if(strlen(a1)<strlen(b1)||(strlen(a1)==strlen(b1)&&strcmp(a1,b1)<0))
{
strcpy(c1,a1);
strcpy(a1,b1);
strcpy(b1,c1);
printf("-");
}
x=strlen(a1);
y=strlen(b1);
for(i=0;i<x;i++) a[i]=a1[x-i-1]-'0';
for(i=0;i<y;i++) b[i]=b1[y-i-1]-'0';
for(i=0;i<100010;i++){
if(a[i]<b[i]){
a[i]=a[i]+10;
a[i+1]--;
}
c[i]=a[i]-b[i];
}
for(i=100010;i>=0;i--){
if(c[i]!=0){
z=i;
break;
}
}
for(i=z;i>=0;i--) printf("%d",c[i]);
return 0;
}
高精度乘法
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
using namespace std;
const int N=100010;
char a1[N],b1[N];
int a[N],b[N],c[N];
int la,lb,lc;
int main()
{
int i,j,x=0;
cin>>a1>>b1;
la=strlen(a1);
lb=strlen(b1);
for(int i=0;i<la;i++) a[la-i]=a1[i]-'0';
for(int i=0;i<lb;i++) b[lb-i]=b1[i]-'0';
for(i=1;i<=la;i++)
for(j=1;j<=lb;j++)
c[i+j-1]+=a[i]*b[j];
for(i=1;i<=la+lb;i++){
if(c[i]>9){
c[i+1]+=c[i]/10;
c[i]%=10;
}
}
lc=la+lb;
while (c[lc]==0&&lc>1) lc--;
for (i=lc;i>=1;i--)cout<<c[i];
return 0;
}
高精度除法
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int N=100010;
char a1[N];
int a[N],c[N];
int b1,la,lc;
int main()
{
while(cin>>a1>>b1){// 1.输入大整数和整数b1
memset(a,0,sizeof a);// 清零
memset(c,0,sizeof c);
// 获取大整数的长度
la=strlen(a1);
for(int i=0;i<la;i++) a[la-i-1]=a1[i]-'0';// 2.将数字串从低位往高位存储到数组a中
// 3.计算c=a/b
int x=0;// 余数初始为0
for(int i=la-1;i>=0;i--){
c[i]=(x*10+a[i])/b1;// 将高位得到的余数乘以10,再加上新位置的数,再除以b
x=(x*10+a[i])%b1;// 得出余数
}
lc=la;
while(lc>1&&c[lc-1]==0)// 4.删去多余的0
lc--;
// 5.从高位往低位输出商
for(int i=0;i<lc;i++) cout<<c[lc-i-1];
// 空格输出余数
cout << " " << x << endl;
}
return 0;
}
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void div(vector<int>& a, int b)
{
vector<int> result;
int r=0;
for(int i=a.size()-1;i>=0;i--)
{
r=r*10+a[i];
result.push_back(r/b);
r%=b;
}
reverse(result.begin(),result.end());
while(result.size()>1 && result.back()==0) result.pop_back();
for(int i=result.size()-1;i>=0;i--) cout<<result[i];
cout<<endl<<r;
}
int main()
{
string a;
int b;
vector<int> c;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) c.push_back(a[i]-'0');
div(c,b);
return 0;
}
阶乘之和
P1009 [NOIP1998 普及组] 阶乘之和
#include<iostream>
using namespace std;
int a[2000],c[2000];
void cheng(int *a,int x)
{
int m=0;
for(int i=1;i<=1000;i++){
a[i]=a[i]*x+m;
m=a[i]/10;
a[i]%=10;
}
}
void pplus(int *a,int *c)
{
int m=0;
for(int i=1;i<=1000;i++){
c[i]=a[i]+c[i]+m;
m=c[i]/10;
c[i]%=10;
}
}
int main()
{
int n;
cin>>n;
a[1]=1;
for(int i=1;i<=n;i++){
cheng(a,i);
pplus(a,c);
}
bool flag=0;
for(int i=1000;i>=1;i--){
if(c[i]!=0) flag=1;
if(flag) cout<<c[i];
}
return 0;
}
前缀和与差分
6.前缀和
Description
输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。
Input
第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
Output
共m行,每行输出一个询问的结果。
Sample Input 1
5 3
2 1 3 6 4
1 2
1 3
2 4
Sample Output 1
3
6
10
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int N=100010;
int a[N],s[N];
int n,m;
int main()
{
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]+a[l]<<endl;
}
return 0;
}
7.子矩阵的和
Description
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
Input
第一行包含三个整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
Output
共q行,每行输出一个询问的结果。
Sample Input 1
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
Sample Output 1
17
27
21
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int N=1010;
int a[N][N],s[N][N];
int n,m,q;
int main()
{
scanf("%d %d %d",&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 x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);//算子矩阵和
}
return 0;
}
8.差分
Description
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
Input
第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
Output
共一行,包含n个整数,表示最终序列。
Sample Input 1
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
Sample Output 1
3 4 5 3 4 2
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=100010;
int a[N],b[N];
int n,m;
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
//insert(i,i,a[i]);
b[i]=a[i]-a[i-1];
}
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
insert(l,r,c);
}
for(int i=1;i<=n;i++){
b[i]+=b[i-1];
cout<<b[i]<<" ";
}
return 0;
}
9.差分矩阵??
Description
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
Input
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
Output
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
Sample Input 1
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
Sample Output 1
2 3 4 1
4 3 4 1
2 2 2 2
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=1010;
int a[N][N],b[N][N];
int n,m,q;
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;
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
insert(i,j,i,j,a[i][j]);
}
}
while(q--)
{
int x1,y1,x2,y2,c;
cin>>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];
if(j==m) cout<<b[i][j];
else cout<<b[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
双指针算法
10. 最长连续不重复子序列
Description
给定一个长度为n的整数序列,请找出最长的不包含重复数字的连续区间,输出它的长度。
Input
第一行包含整数n。
第二行包含n个整数(均在0~100000范围内),表示整数序列。
1≤n≤100000
Output
共一行,包含一个整数,表示最长的不包含重复数字的连续子序列的长度。
Sample Input 1
5
1 2 2 3 5
Sample Output 1
3
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=100010;
int n,ans;
int a[N],s[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
while(s[a[i]]>1){
s[a[j]]--;
j++;
}
ans=max(ans,i-j+1);
}
cout<<ans;
return 0;
}
11.数组元素的目标和
Description
给定两个升序排序的有序数组A和B,以及一个目标值x。数组下标从0开始。
请你求出满足A[i] + B[j] = x的数对(i, j)。
数据保证有唯一解。
Input
第一行包含三个整数n,m,x,分别表示A的长度,B的长度以及目标值x。
第二行包含n个整数,表示数组A。
第三行包含m个整数,表示数组B。
数组长度不超过5000000。
同一数组内元素各不相同。
1≤数组元素≤10^9
Output
共一行,包含两个整数 i 和 j。
Sample Input 1
4 5 6
1 2 4 7
3 4 6 8 9
Sample Output 1
1 1
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=5000010;
int n,m,x;
int a[N],b[N];
int 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];
for(int i=0,j=m-1;i<n;i++){
while(j>0&&a[i]+b[j]>x) j--;
if(a[i]+b[j]==x){
cout<<i<<" "<<j<<endl;
break;
}
}
return 0;
}
位运算
12. 二进制中1的个数
Description
给定一个长度为n的数列,请你求出数列中每个数的二进制表示中1的个数。
Input
第一行包含整数n。
第二行包含n个整数,表示整个数列。
1≤n≤100000,
0≤数列中元素的值≤10^9
Output
共一行,包含n个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中1的个数。
Sample Input 1
5
1 2 3 4 5
Sample Output 1
1 1 2 1 2
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=5000010;
int n;
int lowbit(int x)
{
return x&-x;
}
int main()
{
cin>>n;
while(n--)
{
int x;
cin>>x;
int ans=0;
while(x){
x-=lowbit(x);//lowbit(x)返回x的二进制中最后以为1(包含后面的0)
ans++;//每次减去x的最后以为1
}
cout<<ans<<" ";
}
return 0;
}
离散化
13.区间和
Description
假定有一个无限长的数轴,数轴上每个坐标上的数都是0。
现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。
近下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
Input
第一行包含两个整数n和m。
接下来 n 行,每行包含两个整数x和c。
再接下里 m 行,每行包含两个整数l和r。
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
Output
共m行,每行输出一个询问中所求的区间内数字和。
Sample Input 1
3 3
1 2
3 6
7 5
1 3
4 6
7 8
Sample Output 1
8
0
5
代码:
/*整数离散化两部分,去重和二分
//去重
sort(alls.begin(),alls.end());//将所有值排序
alls.erase(unique(alls.begin(),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,3、、、n
}
*/
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=300010;
int n,m;
int a[N],s[N];
vector<int> alls;//存储所有待离散化的值
vector<PII>add,query;
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,3、、、n
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++){
int x,c;
cin>>x>>c;
add.push_back({x,c});
alls.push_back(x);
}
for(int i=0;i<m;i++){
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()));//去重元素
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];//find函数里面,返回的是r+1,所有这里是i<=alls.size()
//处理询问
for(auto item:query){
int l=find(item.first),r=find(item.second);
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
14.区间合并
Description
给定n个区间[li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3]和[2,6]可以合并为一个区间[1,6]。
Input
第一行包含整数n。
接下来n行,每行包含两个整数 l 和 r。
1≤n≤100000,
−109≤li≤ri≤109
Output
共一行,包含一个整数,表示合并区间完成后的区间个数。
Sample Input 1
5
1 2
2 4
5 6
7 8
7 9
Sample Output 1
3
代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=100010;
int n;
vector<PII> q;
void merge(vector<PII> &q){
vector<PII> res;
sort(q.begin(),q.end());
int st=-2e9,ed=-2e9;
for(auto it:q){
if(ed<it.first){
if(st!=-2e9) res.push_back({st,ed});
st=it.first,ed=it.second;
}
else ed=max(ed,it.second);
}
if(st!=-2e9) res.push_back({st,ed});
q=res;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++){
int l,r;
cin>>l>>r;
q.push_back({l,r});
}
merge(q);
cout<<q.size();
return 0;
}
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010;
int n;
struct node{
int l,r;
}q[N];
bool cmp(node x,node y)
{
return x.l<y.l;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>q[i].l>>q[i].r;
sort(q,q+n,cmp);
int ans=0,st=-2e9,ed=-2e9;
for(int i=0;i<n;i++){
if(q[i].l>ed){
ans++;
ed=q[i].r;
st=q[i].l;
}
else if(ed<q[i].r) ed=q[i].r;//相交
}
cout<<ans;
return 0;
}