C105 整體二分+樹狀陣列 P2617 Dynamic Rankings

董晓發表於2024-03-23

影片連結:C105 整體二分+樹狀陣列 P2617 Dynamic Rankings_嗶哩嗶哩_bilibili

C96 樹狀陣列套權值線段樹 P2617 Dynamic Rankings - 董曉 - 部落格園 (cnblogs.com)

C104【模板】整體二分+樹狀陣列 P3834 可持久化線段樹2 - 董曉 - 部落格園 (cnblogs.com)

Luogu P2617 Dynamic Rankings

// 整體二分+樹狀陣列 O(nlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=300005;
int n,m,cnt,a[N];
int ans[N],s[N];

struct Q{
  //  數: x位置,y值,k貢獻,opt=0  
  //查詢: [x,y]第k小,id編號,opt=1
  int x,y,k,id,opt;
}q[N<<1],q1[N<<1],q2[N<<1];

void add(int x,int v){ //加入貢獻
  while(x<=n)s[x]+=v,x+=x&(-x);
}
int sum(int x){ //字首和
  int t=0;
  while(x)t+=s[x],x-=x&(-x);
  return t;
}
void solve(int l,int r,int L,int R){
  if(l>r) return; //[l,r]資料個數域 [L,R]值域
  if(L==R){
    for(int i=l;i<=r;i++)
      if(q[i].opt) ans[q[i].id]=L; //記錄答案
    return;
  }
  int mid=(L+R)>>1,p1=0,p2=0;
  for(int i=l;i<=r;i++){
    if(!q[i].opt){          //若是數,按值分流
      if(q[i].y<=mid)   
        add(q[i].x,q[i].k), //加入貢獻
        q1[++p1]=q[i];      //分流到左邊
      else q2[++p2]=q[i];   //分流到右邊
    }
    else{                   //若是查詢,按個數分流
      int s=sum(q[i].y)-sum(q[i].x-1);
      if(s>=q[i].k) q1[++p1]=q[i];  //分流到左邊
      else q[i].k-=s,q2[++p2]=q[i]; //分流到右邊
    }
  }
  for(int i=1;i<=p1;i++)
    if(!q1[i].opt) add(q1[i].x,-q1[i].k);  //減去貢獻
  for(int i=1;i<=p1;i++)q[i+l-1]=q1[i]; 
  for(int i=1;i<=p2;i++)q[i+l+p1-1]=q2[i]; //合併陣列
  solve(l,l+p1-1,L,mid);
  solve(l+p1,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m); char ch[2]; int x,y,k;
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),q[++cnt]={i,a[i],1,0,0};
  for(int i=1;i<=m;i++){
    scanf("%s%d%d",ch,&x,&y);
    if(ch[0]=='C')
      q[++cnt]={x,a[x],-1,0,0},  //舊數貢獻-1
      q[++cnt]={x,a[x]=y,1,0,0}; //新數貢獻+1
    else scanf("%d",&k),q[++cnt]={x,y,k,i,1};
  }
  memset(ans,-1,sizeof ans);
  solve(1,cnt,0,1e9); //整體二分
  for(int i=1;i<=m;i++)if(~ans[i])printf("%d\n",ans[i]);
}

相關文章