双指针算法的样子大概是
for(int i=0;i<n;i++){
while(i<j&&check(i))j++;
}
一般可以把O(n2)算法优化到O(n)
下面看一道题
传送门
很巧妙用双指针清除前面的数字
#include<iostream>
using namespace std;
const int N = 1e5+10;
int q[N],a[N];
int main(){
int n,ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=0,j=0;i<n;i++){
q[a[i]]++;
while(q[a[i]]>1){
q[a[j]]--;
j++;
}
ans=max(ans,i-j+1);
}
printf("%d",ans);
return 0;
}
双指针使用需要数组满足单调性
何为单调性
可以看看这题
a,b数组有序
i从a数组的头开始
j从b数组的尾开始
这就满足了单调性
传送门
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
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){
if(a[i]+b[j]>k)j--;
else if(a[i]+b[j]==k){
printf("%d %d\n",i,j);
return 0;
}
else break;
}
}
return 0;
}
关于lowbit的作用
实现它很简单
就是
int lowbit(int x){
return x&-x;
}
实际上-x=~x+1
所以x&(~x+1)
相当于取了最后一个一以及后面的所有0
举个例子
x=(10101000)2
而~ x=(01010111)2
所以~ x+1=(01011000)2
x&(~ x+1)=(1000)2
用lowbit可以计算出一个数的二进制中有几个1
传送门
#include<iostream>
using namespace std;
int lowbit(int x){
return x&-x;
}
int q[100005];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&q[i]);
int res=0;
while(q[i]){
q[i]-=lowbit(q[i]);
res++;
}
printf("%d ",res);
}
return 0;
}
关于离散化
可以看这题
传送门
把所有需要离散化的东西放在alls里面,然后其他的加操作啊,询问操作都先存起来,然后find函数用于询问离散化后的下标
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
vector<int>alls;
vector<PII>add,ask;
int q[300005];
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(){
int n,m,l,r;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d%d",&l,&r);
alls.push_back(l);
add.push_back({l,r});
}
for(int i=0;i<m;i++){
scanf("%d%d",&l,&r);
ask.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:add){
q[find(item.first)]+=item.second;
}
for(int i=1;i<=300000;i++){
q[i]+=q[i-1];
}
for(auto item:ask){
l=find(item.first);
r=find(item.second);
printf("%d\n",q[r]-q[l-1]);
}
return 0;
}
关于区间合并
传送门
类似于贪心
先排序,注意pair排序自动以第一字段和第二字段为关键字
然后就是3种情况的更新区间l,r
到最后如果l,r被更新过的话
一定要再加一个没来得及加上的答案!
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
vector<PII>seg,ans;
int main(){
int n,l,r;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d%d",&l,&r);
seg.push_back({l,r});
}
sort(seg.begin(),seg.end());//pair自动以第一第二字段排序
l=-2e9,r=-2e9;
for(auto item:seg){
if(r<item.first){
if(l!=-2e9)ans.push_back({l,r});
l=item.first,r=item.second;
}else{
r=max(item.second,r);
}
}
if(l!=-2e9)ans.push_back({l,r});
printf("%d",ans.size());
return 0;
}