bzoj4867: [Ynoi2017]舌尖上的由乃

Posted ccz181078

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4867: [Ynoi2017]舌尖上的由乃相关的知识,希望对你有一定的参考价值。

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

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

块大小取$O(\sqrt{n}logn)$时时间复杂度达到最优$O(m\sqrt{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\n",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;
}

 

以上是关于bzoj4867: [Ynoi2017]舌尖上的由乃的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4811[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

BZOJ4810: [Ynoi2017]由乃的玉米田

BZOJ4811 [Ynoi2017]由乃的OJ

BZOJ4810 [Ynoi2017]由乃的玉米田

bzoj 4811: [Ynoi2017]由乃的OJ

bzoj4810: [Ynoi2017]由乃的玉米田