双指针 学习笔记

概述

双指针是一种简单的技巧,通过两个指针维护一些具有单调性、可快速增删的区间信息。

一般要求的是求满足某个条件的最长/最短区间。当一个指针增加时,单调性要求满足这个条件的分界点是单调递增的。枚举一个指针,让另一个指针不断增加直到满足条件即可。两个指针都只会遍历一遍,复杂度 \(O(n)\)

例题

P1638

题意:求最短的子区间使区间中出现了 \([1,m]\) 的所有数。

显然区间中数字出现的个数具有单调性。维护两个指针 \(l,r\)\(l\) 移动时,\(r\) 是单调递增的。枚举 \(l\),每次让 \(r\) 不断增加直到满足条件。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005],cnt[2005],ansl=0,ansr=0x3f3f3f3f;
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int l=1,r=0,now=0;l<=n;){
    while(now<m&&r<n)now+=!cnt[a[++r]]++;
    if(now<m)break;
    if(r-l+1<ansr-ansl+1)ansl=l,ansr=r;
    now-=!--cnt[a[l++]];
  }  
  return cout<<ansl<<' '<<ansr<<'\n',0;
}

UVA11572

题意:求最长的子区间使区间中的数没有重复。

类似上题,但是这题是求最长的区间。这时改为枚举 \(r\),拉动 \(l\) 移动。

#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[1000005],ans;
unordered_map<int,int>cnt;
int main(){
  cin>>t;
  while(t--){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int l=1,r=0;r<=n;){
      while(cnt[a[r]]>1)cnt[a[l++]]--;
      ans=max(r-l+1,ans),cnt[a[++r]]++;
    }  
    cout<<ans<<'\n',ans=0,cnt.clear();
  }
  return 0;
}

Baka's Trick

双指针时要维护当前区间的信息。假如这个信息不支持删除,但支持快速合并,可以用下面的方式:

假设要找满足条件的最长区间,维护指针 \(mid\),并维护两个栈表示 \([l,mid]\) 的后缀和,\((mid,r]\) 的前缀和。当 \(l\) 增加越过 \(mid\) 时,将 \(mid\) 设为 \(r\),清空右栈并枚举 \([l,r]\) 重构左栈。区间信息可以用两个栈内的信息拼起来。

复杂度主要多出了重构部分。每次重构时的 \(l\) 是上次重构时的 \(r+1\),因此重构的区间是不相交的,复杂度 \(O(n)\)

CF1548B

差分后转化为求 \(\gcd>1\) 的最长区间,使用上面的技巧即可。

#include<bits/stdc++.h>
using namespace std;
int t,n,ans;
long long a[200005],st[200005];
template<typename T>T gcd(T m,T n){
  return n?gcd(n,m%n):m;
}
int main(){
  cin>>t;
  while(t--){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    n--;
    for(int i=1;i<=n;i++)a[i]=abs(a[i]-a[i+1]);
    for(int i=1,j=1,mid=0;i<=n;i++){
      st[i]=i==mid+1?a[i]:gcd(st[i-1],a[i]);
      while(j<=mid&&gcd(st[j],st[i])==1)j++;
      if(j>mid){
        mid=i,st[i]=a[i];
        for(int k=i-1;k>=j;k--)st[k]=gcd(a[k],st[k+1]);
        while(j<=i&&st[j]==1)j++;
      }
      ans=max(ans,i-j+1);
    }
    cout<<ans+1<<'\n',ans=0;
    for(int i=1;i<=n;i++)st[i]=0;
  }
  return 0;
}

[[杂项]]

posted @ 2024-03-01 09:42  lgh_2009  阅读(4)  评论(0编辑  收藏  举报