第一章易错的知识点:
① 一维和二维前缀和的模板都是减去-1的
一维:a[l] + … + a[r] = S[r] - S[l - 1] 是S[l-1]不是S[l]这是因为存储是从1开始存的,所以S[l]包括了编辑的a[i]
二维:以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1],也是同理,
此外,二维的
s[i, j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]
② 离散化的模板,alls存的是待离散化的坐标,把它离散成从1开始的连续自然数
——————————————————————————————————————————————————————————————————
1.错题一:数的三次方根
(1) 原题链接:https://www.acwing.com/problem/content/792/
(2)出错原因:
①check函数不知道怎么比。注意,浮点数的模板是和找左边界的类似的,因此是在左边界的右侧的满足条件,即q[mid]>=x。
②else 更新的是l=mid,不是l=mid+1
③注意while的条件是右边减去左边
④精度要比题目要求的还要多一位小数点,因为多多益善,最后只需要保留题目中所需要的位数即可
(3)代码如下:
#include<iostream>
#include<iomanip>
using namespace std;
double check(double mid){
return mid*mid*mid;
}
int main(){
double n;cin>>n;
double esp=1e-7;
double l=-10000,r=10000;
while(r-l>=esp){
double mid=(l+r)/2;
if(check(mid)>=n) r=mid;
else l=mid;
}
//cout<<fixed<<setprecision(6)<<l;
printf("%.6f",l);
}
2.错题二:数的范围
(1) 原题链接:
https://www.acwing.com/problem/content/791/
(2)出错原因:
①注意在找起始与结束位置的时候,其实就是二分找左边界与右边界
②注意在找到左边界的时候要判断是否找的到这位数,如果找不到的话就直接输出结果,否则才继续进行右边界的寻找
③找左边界与右边界的模板不要背混
(3)代码:
{
int n,k; scanf("%d %d",&n,&k);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
while(k--){
int x;scanf("%d",&x);
int l=0,r=n-1;
while(l<r){
int mid=l+r>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[l]!=x) cout<<"-1 -1"<<endl;
else{
cout<<l<<" ";
int l=0,r=n-1;
while(l<r){
int mid=l+r+1>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
}
3.错题三:逆序对的数量
(1)原题链接:
https://www.acwing.com/problem/content/790/
(2)出错原因:
①没有很好的分析出题目的规律:注意用归并的时候,我们发现只需要找出左半边的临界点,即这个点大于该此时右半边的点,那么从左半边临界点之后的点都是逆序对,所以得出公式:对于左半边每一个点Sj,它的逆序对有mid-i+1
②要学会怎么分析数据会不会越界,是用long,long,因为数列最长有n,假设为整个数列都是逆序的,则逆序对有n+n-1+n-2+…+1,即共有n*(n-1)/2,由于n最大为100000,所以最大的逆序对个数可以超过10的9次方,所以用 Long long
(3)代码如下:
#include <iostream>
using namespace std;
const int N=100010;
long long q[N];
long long count=0;
long long temp[N],Orderq[N];
void merge_sort(long long q[],long long l,long long r){
if(l>=r) return;
long long mid=l+r>>1;
merge_sort(q,l,mid);
merge_sort(q,mid+1,r);
long long k=0,i=l,j=mid+1;
while(i<=mid&&j<=r){
if(q[i]<=q[j]) temp[k++]=q[i++];
else{
count=count+mid-i+1;
temp[k++]=q[j++];
}
}
while(i<=mid) temp[k++]=q[i++];
while(j<=r) temp[k++]=q[j++];
for(long long i=0,j=l;j<=r;j++,i++) q[j]=temp[i];
}
int main(){
long long n; scanf("%lld",&n);
for(long long i=0;i<n;i++) scanf("%lld",&q[i]);
for(long long i=0;i<n;i++) Orderq[i]=q[i];
merge_sort(Orderq,0,n-1);
printf("%lld",count);
}
4.错题四:高精度减法
(1) 原题链接:
https://www.acwing.com/problem/content/description/794/
(2)出错原因:
①两个大整数大小判断的方法错误。因为做减法的时候,要比较两个数的大小,如果小的在前面的话,要调转返回并且输出负号的。一开始我是直接想通过两个vector的数组的规格来比较谁大谁小的,但是很显然是忘记了长度相同的情况,因此判断两个大整数的大小的时候,可以先考虑长度是否相同,如果不相同就返回 return A.size()<B.size(),学会这个表达;如果长度相同,就从后往前遍历(即从大到小,这个我当时也错了),当遇到不相同的元素的时候,就返回 return A[i]<B[i]. 其它情况就是,还别忽略,就是当A和B完全相等的时候,就要返回false,不进行掉转调用函数****(这个我当初也忽略了,所以导致当遇到AB完全相同时,进行一个死循环)
int compared(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 0;
}
②忘记去除结果的前导零了。
③忽略了减法,如果A比B小,最后结果要输出一个负号
(3) 代码如下:
#include<iostream>
#include "string.h"
#include <vector>
using namespace std;
vector<int> C;
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 false;**//当A=B时,不进行掉转调用函数**
}
void deletealopy(vector<int> &A,vector<int> &B)
{
if(cmp(A,B)) return deletealopy(B,A);
for(int i=0,t=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size()) t=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();
}
int main()
{
string a,b;cin>>a>>b;
vector<int> A,B;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
deletealopy(A,B);
if(cmp(A,B)) printf("-");
for(int j=C.size()-1;j>=0;j--) cout<<C[j];
}
5.错题五:高精度加法
(1) 原题链接:
https://www.acwing.com/problem/content/795/
(2)出错原因:
我服了,字符串PUSH到Vectord的时候,忘记减去-‘0’了。。。。究其原因还是不熟,而且不是死记硬背,而应该是学会思路。前导零勿忘。
6.错题六:最长不重复子序列
(1)原题链接:
https://www.acwing.com/problem/content/801/
(2)出错原因:
不会利用双指针算法分析这题
(3)题目分析:
首先利用两个指针i和j,i一直往右走,而j也一直往右走,但是不能>i,即j就是为了找出离i左边最远的距离。所以如果遇到了重复元素的时候,j就向前一格,这样就保证了j一直都是指向不重复序列中的开始元素。
(4)代码如下:
注意:s数组下标表示了原数组的数据,因此它是用来存储原数组的数据的个数的,如果个数大于=2就说明有重复的。
#include <iostream>
using namespace std;
const int N=100010;
int a[N],s[N];
bool check(int i,int j)
{
if(a[j]!=a[i]) return true;
else return false;
}
int main()
{
int n;scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int res=0;
for(int i=0,j=0;i<n;i++)
{
s[a[i]]++;
while(s[a[i]]>1)
{
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
printf("%d",res);
return 0;
}
7.错题7:双指针算法
(1)原题链接:
https://www.acwing.com/problem/content/802/
(2)出错原因
分析的方法错了,该题目设置的两个指针应该是一个从头开始一个从尾开始,这样的话就有单调性了
(3)利用双指针算法的解题思路:
先列出该题目暴力的解法,然后观察有无单调性,如果有的话就可以利用双指针进行时间复杂度的化简。
(4)该题目的正确解题思路
(5)代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,x;
int a[N],b[N];
int main()
{
scanf(“%d%d%d”,&n,&m,&x);
for(int i=0;i<n;i++) scanf(“%d”,&a[i]);
for(int i=0;i<m;i++) scanf(“%d”,&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)
{
printf(“%d %d”,i,j);
break;
}
}
return 0;
}
8.错题八:离散化
(1)原题链接:
https://www.acwing.com/problem/content/804/
(2)题目分析:
本题目一开始就是想用前缀和去做的,但是后面发现元素并不能用数组的进行表示,因为跨度太大,数轴的区间长度长达2乘10的九次方,所以开不了数组,但是我们发现一共处理的操作和查询的操作范围才是n+2m,大概范围才30万左右,因此很明显要用离散化,将无限的数轴映射到1,2,3__n,然后再利用前缀和进行求解。
因此要熟背了离散化模版,此外,要学会如何定义一个pair元素对。
(3)代码如下:
include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=300010;
typedef pair<int,int> PII;
vector<int>alls;
vector<PII>adds,query;
int n,m;
int a[N],s[N];
int find(int 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;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
int x,c;scanf("%d%d",&x,&c);
adds.push_back({x,c});//存入需要操作的信息
alls.push_back(x);//将需要操作的元素存入到数组中,
}
for(int i=0;i<m;i++)
{
int l,r;scanf("%d%d",&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:adds){
int k=find(item.first);
a[k]+=item.second;
}
//预处理前缀和:alls映射到a数组下标是从1开始一直到n的
for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];
//处理查询
for(auto item:query){
int l=find(item.first);
int r=find(item.second);
printf("%d\n",s[r]-s[l-1]);
}
return 0;
}