zoukankan      html  css  js  c++  java
  • bzoj4867: [Ynoi2017]舌尖上的由乃

    用dfs序转为区间加,区间第k大

    分块,块内维护排序后的权值,并记录每个权值原来在块中的位置。加法操作对于整块可以打标记,零散部分因为记了每个值排序前的位置,可以直接提取出块中待修改的部分,修改后用归并排序线性重构这个块,对于查询,先把零散部分提取出来,当作普通的块处理,然后二分答案,在每个块上再二分<k的个数。

    块大小取$O(sqrt{n}logn)$时时间复杂度达到最优$O(msqrt{n}logn)$。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    const int N=100007;
    char buf[10000000],*ptr=buf-1;
    int _(){
        int x=0,c=*++ptr;
        while(c<48)c=*++ptr;
        while(c>47)x=x*10+c-48,c=*++ptr;
        return x;
    }
    int n,m,len,B;
    int es[N],enx[N],e0[N],ep=2,ee[N];
    int id[N][2],idp=0;
    int ls[N],rs[N],ws[N];
    struct val{
        int w,v;
    }a[N],a1[N],a2[N],a0[N];
    int aa[N];
    bool operator<(const val&a,const val&b){
        return a.v<b.v;
    }
    void f1(int w,int dep){
        id[w][0]=++idp;
        a[idp]=(val){idp,dep+=ee[w]};
        for(int i=e0[w];i;i=enx[i])f1(es[i],dep);
        id[w][1]=idp;
    }
    void merge(val*v1,int m1,val*v2,int m2,val*v){
        int p1=0,p2=0;
        while(p1<m1&&p2<m2)*v++=v1[p1].v<v2[p2].v?v1[p1++]:v2[p2++];
        while(p1<m1)*v++=v1[p1++];
        while(p2<m2)*v++=v2[p2++];
    }
    void add(int w,int L,int R,int k){
        int l=ls[w],r=rs[w],p1=0,p2=0;
        int aa1=aa[w]+k,aa2=aa[w];
        aa[w]=0;
        for(int i=l;i<=r;++i){
            if(L<=a[i].w&&a[i].w<=R)(a1[p1++]=a[i]).v+=aa1;
            else (a2[p2++]=a[i]).v+=aa2;
        }
        merge(a1,p1,a2,p2,a+l);
    }
    void add(int L,int R,int k){
        int l=ws[L],r=ws[R];
        add(l,L,R,k);
        if(l!=r)add(r,L,R,k);
        for(int i=l+1;i<r;++i)aa[i]+=k;
    }
    int get(int w,int L,int R,val*v){
        int l=ls[w],r=rs[w],p=0;
        for(int i=l;i<=r;++i)if(L<=a[i].w&&a[i].w<=R)(v[p++]=a[i]).v+=aa[w];
        return p;
    }
    void mins(int&a,int b){if(a>b)a=b;}
    void maxs(int&a,int b){if(a<b)a=b;}
    int lss(val*a,int p,int x){
        int L=0,R=p;
        while(L<R){
            int M=L+R>>1;
            if(a[M].v<=x)L=M+1;
            else R=M;
        }
        return L;
    }
    void query(int L,int R,int k){
        if(R-L+1<k){
            puts("-1");
            return;
        }
        int l=ws[L],r=ws[R],p;
        if(l==r)p=get(l,L,R,a0);
        else{
            int p1=get(l,L,R,a1);
            int p2=get(r,L,R,a2);
            merge(a1,p1,a2,p2,a0);
            p=p1+p2;
        }
        int mn=0x7fffffff,mx=-mn;
        if(p)mn=a0[0].v,mx=a0[p-1].v;
        for(int i=l+1;i<r;++i)mins(mn,a[ls[i]].v+aa[i]),maxs(mx,a[rs[i]].v+aa[i]);
        while(mn<mx){
            int x=mn+(mx-mn>>1);
            int c=lss(a0,p,x);
            for(int i=l+1;i<r;++i)c+=lss(a+ls[i],rs[i]-ls[i]+1,x-aa[i]);
            if(c<k)mn=x+1;
            else mx=x;
        }
        printf("%d
    ",mn);
    }
    int main(){
        fread(buf,1,sizeof(buf),stdin)[buf]=0;
        n=_();m=_();len=_();
        B=sqrt(n+1)*log2(n+1)*0.371+1;
        for(int i=2;i<=n;++i){
            int f=_();
            ee[i]=_();
            es[ep]=i;enx[ep]=e0[f];e0[f]=ep++;
        }
        f1(1,0);
        for(int l=1,r=B,c=1;l<=n;l+=B,r+=B,++c){
            if(r>n)r=n;
            for(int i=l;i<=r;++i)ws[i]=c;
            ls[c]=l;rs[c]=r;
            std::sort(a+l,a+r+1);
        }
        while(m--){
            if(_()==1){
                int x=_(),k=_();
                query(id[x][0],id[x][1],k);
            }else{
                int x=_(),k=_();
                add(id[x][0],id[x][1],k);
            }
        }
        return 0;
    }
  • 相关阅读:
    spring入门
    Page.Load和Page_Load差异
    先写alert('提示语句!') 后写Redirect语句,为什么只是跳转而不显示提示语句框
    session.close() session.clear() session.abandon()区别
    关于用户退出,点击浏览器后退仍可回到原来页面
    SQL将一个表中查询语句插入另一张表中的某一列
    复习
    读取xml文件或者项目文件***.csproj 时,出现给定编码中的字符无效。
    电子公文传输系统 团队作业(五):冲刺总结(第四天)
    缓冲区溢出漏洞实验
  • 原文地址:https://www.cnblogs.com/ccz181078/p/7416310.html
Copyright © 2011-2022 走看看