zoukankan      html  css  js  c++  java
  • Luogu P2617 Dynamic Rankings(整体二分)

    题目
    动态区间第K小模板题。
    一个非常可行的办法是BIT套动态开点权值SegTree,但是它跑的实在太慢了。
    然后由于这题并没有强制在线,所以我们可以使用整体二分来吊打树套树。
    当然如果强制在线的话就只能够用大常数的树套树了。
    这里稍微结合这道题讲一下整体二分吧。
    我们把所有询问加修改(统称为操作)按时间顺序排好,然后一起二分答案。
    对于一个操作区间([ql,qr])和答案(mid),我们顺序遍历所有操作。
    将所有能在当前情况下产生影响的修改进行执行并放到左操作区间,否则直接放到右操作区间。
    将所有答案(le mid)的询问放到左操作区间,否则在处理完当前执行的操作的影响之后放到右区间。
    然后把我们用来维护修改求答案的数据结构还原。并对左操作区间和右操作区间分别分治。
    因为我们考虑这样一个事实:不管我们分治到了什么时候,所有操作区间中的操作还是按时间排序的。
    因此我们可以直接顺序遍历操作来修改和查询。
    而在考虑左操作区间的修改对右操作区间的查询的影响时,我们可以直接在遍历的过程中进行修改。
    在这个题目中,我们二分出来一个答案(mid),那么我们把所有(le mid)的数视作(1),否则视作(0),那么我们就可以根据前缀和来判断答案是(le mid)还是(>mid)了。
    我们可以将初始序列看做在某个位置插入一个数,修改看做在某个位置把原来的数删掉再插入一个新的。
    那么我们就可以使用BIT来支持单点修改和区间查询。
    注意在清零BIT的时候是要撤销而非memset,否则复杂度原地升天。

    #include<bits/stdc++.h>
    using namespace std;
    namespace IO
    {
        char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
        char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
        void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
        void Put(char x){*oS++=x;if(oS==oT)Flush();}
        char fetch(){char c=Get();while(!isupper(c))c=Get();return c;}
        int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
        void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
    ');}
    }
    using namespace IO;
    const int N=100007;
    struct node{int opt,l,r,x,id;}q[N*3],q1[N*3],q2[N*3];
    int n,m,Q,tot,a[N],h[N<<1],t[N],ans[N*3];
    void add(int p,int v){for(;p<=n;p+=p&-p)t[p]+=v;}
    int ask(int p){int s=0;for(;p;p^=p&-p)s+=t[p];return s;}
    void solve(int ql,int qr,int vl,int vr)
    {
        if(ql>qr) return ;
        if(vl==vr)
        {
    	for(int i=ql;i<=qr;++i) if(q[i].opt==2) ans[q[i].id]=h[vl];
    	return ;
        }
        int mid=(vl+vr)>>1,l=0,r=0;
        for(int i=ql,k;i<=qr;++i)
    	if(q[i].opt==1)
    	    if(q[i].r<=mid) add(q[i].l,q[i].x),q1[l++]=q[i];
    	    else q2[r++]=q[i];
    	else
    	    if((k=ask(q[i].r)-ask(q[i].l-1))>=q[i].x) q1[l++]=q[i];
    	    else q[i].x-=k,q2[r++]=q[i];
        for(int i=0;i<l;++i) if(q1[i].opt==1) add(q1[i].l,-q1[i].x);
        memcpy(q+ql,q1,l*(sizeof(node))),memcpy(q+ql+l,q2,r*(sizeof(node)));
        solve(ql,ql+l-1,vl,mid),solve(ql+l,qr,mid+1,vr);
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;++i) h[++tot]=a[i]=read(),q[++Q]=(node){1,i,a[i],1,Q};
        for(int i=1,x,l,r;i<=m;++i)
    	if(fetch()=='Q') l=read(),r=read(),x=read(),q[++Q]=(node){2,l,r,x,Q};
    	else l=read(),h[++tot]=r=read(),q[++Q]=(node){1,l,a[l],-1,Q},q[++Q]=(node){1,l,a[l]=r,1,Q};
        sort(h+1,h+tot+1),tot=unique(h+1,h+tot+1)-h-1;
        for(int i=1;i<=n;++i) a[i]=lower_bound(h+1,h+tot+1,a[i])-h;
        for(int i=1;i<=Q;++i) if(q[i].opt==1) q[i].r=lower_bound(h+1,h+tot+1,q[i].r)-h;
        solve(1,Q,1,tot);
        for(int i=1;i<=Q;++i) if(ans[i]) write(ans[i]);
        return Flush(),0;
    }
    
  • 相关阅读:
    IntelliJ IDEA常用统一设置2-Inspections检查设置(Linux/Mac/Windows)
    IntelliJ IDEA版本:Ultimate、Community、EAP版本的区别
    IntelliJ IDEA重构技巧收集
    Java泛型中的类型擦除机制简单理解
    阿里巴巴Java开发手册中的DO、DTO、BO、AO、VO、POJO定义
    Java中PO、BO、VO、DTO、POJO、DAO概念及其作用和项目实例图(转)
    Java使用logback记录日志时分级别保存文件
    Java中List,Set和Map详解及其区别和使用场景(转)
    Java中泛型的Class<Object>与Class<?>的区别(转)
    Java中泛型T和Class<T>以及Class<?>的理解(转)
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11938288.html
Copyright © 2011-2022 走看看