### Day6: 1.202006-2 2.202009-2 3.202012-2
#### 1.202006-2:稀疏向量(map哈希,双指针(但一开始未用到))
(1)原来想两个数组暴力,但只能得60分,因为数组要存储连续数据,但用map能得100分,因为map用于存储稀疏数据比较有效
```
//1.671s
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,a,b;
cin>>n>>a>>b;
long long res=0;
map<int,int> mpa;
map<int,int> mpb;
for(int i=0;i<a;i++){
int x,y;
cin>>x>>y;
mpa[x]=y;
}
for(int i=0;i<b;i++){
int x,y;
cin>>x>>y;
mpb[x]=y;
}
if(mpa.size()>mpb.size()){
swap(mpa,mpb);
}
for(auto it=mpa.begin();it!=mpa.end();it++){
int x=it->first;
if(mpb.find(x)!=mpb.end()){
res+=mpa[x]*mpb[x];
}
}
cout<<res;
return 0;
}
```
(2)优化:
1.不需要两个map,只需要一个map存储a,然后遍历b的时候计算res即可
2.以后刷题尽量用scanf和printf,更快
```
//1.015s
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,a,b,x,y;
scanf("%d%d%d",&n,&a,&b);
long long res=0;
map<int,int> mpa;
for(int i=0;i<a;i++){
scanf("%d%d",&x,&y);
mpa[x]=y;
}
for(int i=0;i<b;i++){
scanf("%d%d",&x,&y);
res+=mpa[x]*y;//如果访问一个不存在的键,`map` 会自动创建该键,并将其值初始化为默认值(对于 `int` 类型,默认值为 `0`)。
}
printf("%lld",res);
return 0;
}
```
(2)学习**双指针**(直观):
遍历两个**有序**列表(在本题中是稀疏向量的非零元素列表)时**减少不必要的比较和迭代**。
```
//921ms
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,a,b;
scanf("%d%d%d",&n,&a,&b);
vector<pair<int,int>> va(a);
vector<pair<int,int>> vb(b);
for(int i=0;i<a;i++){
scanf("%d%d",&va[i].first,&va[i].second);
}
for(int i=0;i<b;i++){
scanf("%d%d",&vb[i].first,&vb[i].second);
}
long long res=0;
int i=0,j=0;
while(i<a && j<b){
if(va[i].first==vb[j].first){
res+=va[i].second*vb[j].second;
i++;
j++;
}
else if(va[i].first<vb[j].first){
i++;
}
else{
j++;
}
}
printf("%lld",res);
return 0;
}
```
**双指针方法**在特定情况下比使用 `map` 方法快得多,尤其是当两个向量的非零元素按顺序排列时。这种方法不仅降低了时间复杂度,还能最大化地利用现代处理器的缓存架构。
#### 2.202009-2:风险人群筛查(小模拟)
简单,过(100)
```
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,k,t,xl,yd,xr,yu,x,y;
scanf("%d %d %d %d %d %d %d",&n,&k,&t,&xl,&yd,&xr,&yu);
int pass=0,stay=0;
while(n--){
int staycnt=0;
bool isPass=false;
bool isStay=false;
for(int i=0;i<t;i++){
scanf("%d %d",&x,&y);
if(x>=xl && x<=xr && y>=yd && y<=yu){
isPass=true;
staycnt++;
}
else{
staycnt=0;
}
if(staycnt>=k){
isStay=true;//注意下面不能加break,不然数据输入对应有问题
}
}
if(isPass){
pass++;
}
if(isStay){
stay++;
}
}
printf("%d\n%d",pass,stay);
return 0;
}
```
#### 3.202012-2:期末预测之最佳阈值(排序+前缀和)
(1)自己做50分
(2)学习:
##### 1.可读性好的结构体 [CCF CSP认证 2020-12-2 期末预测之最佳阈值 题解及满分代码(C++11)](https://blog.csdn.net/weixin_49070253/article/details/123202976?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172468561016800211539993%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fvipall.%2522%257D&request_id=172468561016800211539993&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~vipall~first_rank_ecpm_v1~hot_rank-10-123202976-null-null&utm_term=202012-2%EF%BC%9A%E6%9C%9F%E6%9C%AB%E9%A2%84%E6%B5%8B%E4%B9%8B%E6%9C%80%E4%BD%B3%E9%98%88%E5%80%BC&spm=1018.2226.3001.4187)
**关键题目理解**:对于每一个阈值t,所有 y<t 的学生的result 若为0,则算预测正确;所有y>=t 的学生的result 若为1,则算预测正确。
因此,求阈值的预测正确次数就变成了分别统计y<t 的学生中result=0 的个数和y>=t 的学生中result=1 的个数,再相加。
因为需要分别统计y<t 和y>=t 的情况,因此我们可以在计算之前先将结构体数组**按y递增排序**,这样可以从小到大选取阈值,并且方便统计以 y 为界的左右两边的情况。
**前缀和优化**:我们完全可以运用类似前缀和的方法,在选取阈值之前,通过一次正向和一次逆向遍历数组,计算出对每一个元素 stu[i], stu[1]到stu[i-1]中 result=0 的个数和 stu[i]到stu[m]中result=1 的个数。(**注意**:前缀和和前缀积第0号元素的值要注意,不行可以自主添加)
**满分代码**(自己理解手写一遍,跟网站里面的代码有稍许不一样):
```
#include <bits/stdc++.h>
using namespace std;
struct Student{
int y,res;
int before,after;
};
bool cmp(const Student& a,const Student& b){
if(a.y==b.y) return a.res<b.res;
return a.y<b.y;
}
int main(){
int m;
scanf("%d",&m);
vector<Student> v(m+1);
//添加第一个元素,方便前缀和计算,边界不用考虑
v[0].y=-1;
v[0].res=1;
v[0].before=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&v[i].y,&v[i].res);
v[i].before=0;
v[i].after=0;
}
sort(v.begin(),v.end(),cmp);
//前缀和计算前面res为0的个数
for(int i=1;i<=m;i++){
v[i].before=v[i-1].before+(v[i-1].res==0);
}
//类似于前缀和计算后边(包括自己)res为1的个数
if(v[m].res==1){
v[m].after=1;
}
for(int i=m-1;i>=1;i--){//注意边界,因此之前要先判断v[m].after
v[i].after=v[i+1].after+(v[i].res==1);
}
int besty=0,maxcnt=-1;
for(int i=1;i<=m;i++){
int cnt=v[i].before+v[i].after;
if(cnt>=maxcnt){
besty=v[i].y;
maxcnt=cnt;
}
while(v[i].y==v[i+1].y && i<m){
//注意同一个y只要计算一次
i++;
}
}
printf("%d",besty);
return 0;
}
```
##### 2.效率和简洁性的二维数组 [AcWing 3298. 期末预测之最佳阈值 - AcWing](https://www.acwing.com/solution/content/101956/)
**关键**:a[0][i]和a[1][i]分别记录前i个数(不包括自己)中0和1的个数
**注意**:const int N=100010初始化不要错了,一开始写的10010小了之后要不断分配空间会超时
```
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int a[2][N];//a[0][i]和a[1][i]分别记录前i个数(不包括自己)中0和1的个数
int main(){
int m;
scanf("%d",&m);
vector<pair<int,int>> v(m+1);
v[0].first=-1;
v[0].second=-2;
for(int i=1;i<=m;i++){
scanf("%d%d",&v[i].first,&v[i].second);
}
sort(v.begin()+1,v.end());
for(int i=1;i<=m;i++){
//前缀和
a[0][i]=a[0][i-1]+(v[i-1].second==0);
a[1][i]=a[1][i-1]+(v[i-1].second==1);
}
int besty=0,maxcnt=0;
for(int i=1;i<=m;i++){
//使用前缀和,记得补最后的条件
int cnt=a[0][i]+a[1][m]-a[1][i]+(v[m].second==1);
if(cnt>=maxcnt){
besty=v[i].first;
maxcnt=cnt;
}
while(i<m && v[i].first==v[i+1].first){
i++;
}
}
printf("%d",besty);
return 0;
}
```