C86 树状数组+二分 P2161 [SHOI2009] 会场预约

 

视频链接:272 树状数组+二分 P2161 [SHOI2009] 会场预约_哔哩哔哩_bilibili

 

 

Luogu P2161 [SHOI2009] 会场预约

// 树状数组+二分 O(n*logn*logn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define lowb(x) (x&-x)
#define mid ((l+r)>>1)
const int N=100005;
int n,tot,s[N*2],ed[N*2];
//s[x]:左端点在[x-lowb(x)+1,x]内的区间个数

void change(int x,int k){ //向后修
  while(x<=N) s[x]+=k,x+=lowb(x);
}
int query(int x){ //向前查
  int t=0;
  while(x) t+=s[x],x-=lowb(x);
  return t;
}
int main(){
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
    char c; scanf(" %c",&c);
    if(c=='A'){
      int L,R,cnt=0; scanf("%d%d",&L,&R);
      while(1){
        int l=-1,r=R+1;//二分找重叠区的左端点
        while(l+1<r)
          query(mid)>=query(R)?r=mid:l=mid;
          
        if(ed[r]>=L){   //有重叠则删除
          change(r,-1); //区间[r,N]-1
          cnt++; //重叠区间数+1
          tot--; //总区间数-1
        }
        else break; //无重叠则退出
      }
      printf("%d\n",cnt);
      change(L,1); //区间[L,N]+1
      ed[L]=R;     //存区间[L,R]
      tot++;       //总区间数+1
    }
    else printf("%d\n",tot);
  }
}

 

// 平衡树 O(nlong)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;

struct seg{
  int l,r;
  bool operator<(const seg &b)const{
    return r<b.l;
  }
  //a<b:a完全在b的左边; a==b:a与b有重叠
};
set<seg> s; //有序集

int main(){
  int n; cin>>n;
  while(n--){
    char c; scanf(" %c",&c);
    if(c=='A'){
      int l,r,cnt=0; scanf("%d%d",&l,&r);
      seg t={l,r};
      set<seg>::iterator it=s.find(t);
      while(it!=s.end()){
        ++cnt; 
        s.erase(it);
        it=s.find(t);
      }
      s.insert(t);
      printf("%d\n",cnt);
    }
    else printf("%d\n",s.size());
  }
}

 

树状数组维护区间个数,还有这道题:

Luogu P5677 [GZOI2017] 配对统计

// 树状数组+排序 O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define N 300005
#define LL long long
#define lowb(x) x&-x
int n,m,cnt; //cnt:记录好对个数
LL ans,s[N]; //s[x]:左端点在[x−lowb(x)+1,x]内的好对个数

struct A{
  LL v; int pos;
}a[N]; //
struct P{
  int l,r;
}p[N*2]; //好对
struct Q{
  int l,r,pos;
}q[N]; //询问
bool cmp1(A a,A b){ //数按值排序
  return a.v<b.v;
}
bool cmp2(P a,P b){ //好对按右端点排序 
  return a.r!=b.r?a.r<b.r:a.l<b.l;
}
bool cmp3(Q a,Q b){ //询问按右端点排序 
  return a.r!=b.r?a.r<b.r:a.l<b.l;
}
void add(A a,A b){ //好对位置小的放前面
  p[++cnt].l=min(a.pos,b.pos);
  p[cnt].r=max(a.pos,b.pos);
}
void change(int x,int k){ //向后修
  while(x<=n) s[x]+=k,x+=lowb(x);
}
int query(int x){ //向前查
  int t=0;
  while(x) t+=s[x],x-=lowb(x);
  return t;
}
int main(){
  scanf("%d%d",&n,&m);
  if(n==1){puts("0"); return 0;}
  for(int i=1; i<=n; i++)
    scanf("%lld",&a[i].v),a[i].pos=i;
  sort(a+1,a+1+n,cmp1); //数按值排序
  
  add(a[1],a[2]); add(a[n],a[n-1]);
  for(int i=2; i<n; i++){
    int ld=a[i].v-a[i-1].v, rd=a[i+1].v-a[i].v;
    if(ld<rd) add(a[i],a[i-1]);
    else if(ld>rd) add(a[i],a[i+1]);
    else add(a[i],a[i-1]),add(a[i],a[i+1]);
  }
  sort(p+1,p+1+cnt,cmp2); //好对按右端点排序
  
  for(int i=1;i<=m;i++)
    scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=i;
  sort(q+1,q+1+m,cmp3); //询问按右端点排序
  
  //每次qr右移,将右端点<=qr的好对都加入到树状数组里,
  //再减去左端点在[1,ql−1]内的好对个数就是答案
  for(int i=1,j=1; i<=m; i++){
    for(;p[j].r<=q[i].r&&j<=cnt; j++)change(p[j].l,1);
    ans+=1ll*q[i].pos*(j-1-query(q[i].l-1));
  }
  printf("%lld",ans);
}

 

posted @ 2024-01-02 17:21  董晓  阅读(200)  评论(0编辑  收藏  举报