【学习笔鸡】整体二分(P2617 Dynamic Rankings)

【学习笔鸡】整体二分(P2617 Dynamic Rankings)

可以解决一些需要树套树才能解决的问题,但要求询问可以离线。

首先要找到一个具有可二分性的东西,比如区间\(k\)大,就很具有二分性。具体流程是这样的:

  • 假设当前分治是已知当前分治中的询问的范围是\([l,r]\),现在要进一步确定每个询问的范围。二分一个\(mid={l+r\over 2}\)出来,继续确定当前分治中心中每个询问的答案是大于还是小于\(mid\),若小于\(mid\)就放入左边递归,否则去右边递归。对于修改操作,我们同样地递归。注意对于修改拆分,将一个修改变为-1和+1。需要预处理每个修改前后的值是多少。
  • 撤回修改,继续递归

分析复杂度:

  • 对于分治操作,每次问题减半,那么就是\(T(n)=2T(\dfrac n 2)+F(n)\)\(F(n)\)是处理当前分治答案的时间复杂度。那么我们只要设计一个好的算法,使得可以在优秀的时间内处理当前层就可以行了。
  • 对于每个询问,每个询问被处理都是一条\(\log\)的链

时间复杂度看个人实现,好的实现可以做到\(O(\log n)\)的时间复杂度

建议写的时候和写线段树的递归方法统一。 不知道为啥我这份代码写得这么快,Hong Kong journalist

//@winlere
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=1e5+5;
int sav[maxn<<1],len,n,m,cnt,data[maxn],seg[maxn],ans[maxn];
inline void add(const int&pos,const int&tag){for(int t=pos;t<=len&&t>0;t+=t&-t) seg[t]+=tag;}
inline int que(const int&pos){ int ret=0; for(int t=pos;t>0;t-=t&-t) ret+=seg[t]; return ret; }
struct DATA{int ty,l,r,k; DATA(){ty=l=r=k=0;} DATA(int a,int b,int c,int d){ty=a;l=b;r=c;k=d;}};
vector<DATA> q;
inline void addc(const int&pos,const int&tag){
      q.push_back(DATA(0,data[pos],-1,pos));
      q.push_back(DATA(0,tag,1,pos));
      data[pos]=tag; sav[++cnt]=tag;
}

void divd(const int&l,const int&r,vector<DATA>&ve){
      if(l==r||ve.empty()) {for(const auto&t:ve) ans[t.ty]=l; return;}
      vector<DATA> lef,rgt;
      int mid=(l+r)>>1,temp;
      for(auto&t:ve)
        if(!t.ty)
          if(t.l<=mid) add(t.k,t.r),lef.push_back(t);
          else rgt.push_back(t);
        else
          if((temp=que(t.r)-que(t.l-1))>=t.k) lef.push_back(t);
          else t.k-=temp,rgt.push_back(t);
      for(auto&t:lef) if(!t.ty) add(t.k,-t.r);
                
      
      divd(l,mid,lef); divd(mid+1,r,rgt);
}

int main(){
      n=qr(); m=qr();
      memset(data,-1,sizeof data);
      for(int t=1;t<=n;++t) q.push_back(DATA(0,sav[++cnt]=data[t]=qr(),1,t));
      for(int t=1,t1,t2,t3;t<=m;++t) {
        static char c[20];
        scanf("%s",c+1);
        if(c[1]=='C') t1=qr(),t2=qr(),addc(t1,t2);
        if(c[1]=='Q') t1=qr(),t2=qr(),t3=qr(),q.push_back(DATA(t,t1,t2,t3));
      }
      sort(sav+1,sav+cnt+1);
      len=unique(sav+1,sav+cnt+1)-sav-1;
      for(auto&t:q)
        if(!t.ty)
          t.l=lower_bound(sav+1,sav+len+1,t.l)-sav;
      divd(1,len,q);
      for(int t=1;t<=m;++t) if(ans[t]) printf("%d\n",sav[ans[t]]);
      return 0;
}

转载于:https://www.cnblogs.com/winlere/p/11504859.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值