题目1:连号区间数
题意:题目给定一个区间,问有多少个子区间,满足在区间内的数字是连续的,比如像1,2,3就是连续的,1,2,4,就是断开的,从3这里断开。
思路:
暴力做法是枚举区间长度,查看区间是否满足要求,显然复杂度过大。
挖掘题目信息,发现题目给出的数字是一个排列,那么意味着数字不会重复,所以对于一段区间,只需要知道其最小值和最大值,并计算出差值就可以判断区间是否连续。
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int p[10010];
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>p[i];
int res = 0;
for(int i=0;i<n;i++){
int maxi = p[i],mini = p[i];
for(int j=i;j<n;j++){
maxi = max(maxi,p[j]);
mini = min(mini,p[j]);
// if(j == 2) cout<<maxi<<" "<<mini<<endl;
if((maxi-mini) == (j-i)){
// cout<<"["<<i+1<<","<<j+1<<"]"<<" ";
res++;
}
}
// cout<<endl;
}
cout<<res<<endl;
return 0;
}
题目2:递增三元组
题意:给出三个数组,问满足第一个数组中的数字<第二个数组中的数字<第三个数组中的数字的组合有多少。
思路:
暴力做法是枚举每个数组的每个数字,由于每个数组的大小为1e5,时间复杂度明显超限。
若要满足Ai<Bi<Ci,明显Bi起到了桥梁作用,连接了A数组和C数组,所以我们考虑枚举B数组中的元素,发现只要找到小于Bi的A数组中的元素数量和大于Bi的C数组中的元素数量,相乘就是对于Bi元素的答案。对于不同的Bi,累加就是最终答案。注意最终答案个数可能为N^3,所以要使用long long来存储,会爆int。
而求比Bi小/大的数字有多少个,有两种方法。
方法1:前缀和
前缀和本身是判断某个区间的数的和。这里可以用来判断数值从1-Bi个数的和。方法是先使用cnt数组存储每个数值数字的个数,比如cnt[1]表示数值为1的数字有多少个,再使用s数组计算前缀和进行统计,s[n] = cnt[n] + cnt[n-1] ... + cnt[1]。 为了减少边界问题,要令所有数字都加一。
方法2:排序+二分
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N];
int b[N];
int c[N];
int cnt[N];
int s[N];
int as[N],cs[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i],a[i]++;
for(int i=0;i<n;i++) cin>>b[i],b[i]++;
for(int i=0;i<n;i++) cin>>c[i],c[i]++;
for(int i=0;i<n;i++) cnt[a[i]]++;
for(int i=1;i<N;i++) s[i] = s[i-1]+cnt[i];
for(int i=0;i<n;i++) as[i] = s[b[i] - 1];
// cout<<as[1]<<endl;
memset(cnt,0,sizeof cnt);
memset(s,0,sizeof s);
for(int i=0;i<n;i++) cnt[c[i]]++;
for(int i=1;i<N;i++) s[