2019牛客暑期多校训练营(第七场)E-Find the median(动态中位数)

题意:一个初始为空的序列,n次插入,每次插入[L,R]内的所有数字,每次插入后询问中位数。
昨日过掉C后看了一眼E题,一看取模以为是个强制在线的题,再一看数据范围,wdnmd 1 0 9 10^9 109那咋做嘛,就咕咕咕了,结果赛后看了一眼ppt他说离线??然后又看了一眼题,发现这玩意不是强制在线的,然后就顺理成章的秒掉了。
其实还是很好写的,我们维护两个东西,区间内数字出现次数和,以及区间内数字被整体覆盖了多少次。
假设离散化后的数字是:1000,3000,10000,500000。那么线段树底层节点存的是:
[1000,1000],(1000,3000],(3000,10000],(10000,500000]出现的次数,也就是每一个底层节点都存了一个左开右闭的区间内数字的次数。
那么修改操作可以拆成两个操作,一个是单点修改左端点,一个是给区间[L+1,R]内的数字加1。
查询的话,就很像主席树查询第k大那个样子,左子树不行了再去右子树查,当查到底层节点的时候,如果这个节点为1,那么直接返回W[1](离散化后的数组),否则就需要一个小公式来计算了:
当 前 要 查 询 的 次 数 为 x , c i [ ] 为 区 间 数 字 整 体 被 加 的 次 数 , w [ l − 1 ] + x / c i [ k ] + ( x m o d    c i [ k ] ? 1 : 0 ) 当前要查询的次数为x,ci[]为区间数字整体被加的次数,w[l-1]+x/ci[k]+(x \mod ci[k]?1:0) x,ci[]w[l1]+x/ci[k]+(xmodci[k]?1:0)
像这种一个点维护一个区间的题还有一道较简单的:CodeForces - 915E

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=4e5+7;

int L[maxn],R[maxn];
int X[maxn],Y[maxn];

int m;

int w[maxn<<1];

void quchong(int n){
    sort(w+1,w+1+n);
    m=unique(w+1,w+1+n)-w-1;
}

int getid(int x){
    return lower_bound(w+1,w+1+m,x)-w;
}

ll lazy[maxn<<3|1];
ll sum[maxn<<3|1];
ll ci[maxn<<3|1];

void build(int l,int r,int k){
    lazy[k]=sum[k]=ci[k]=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}

void pushup(int k){
    sum[k]=sum[k<<1]+sum[k<<1|1];
}

void pushdown(int l,int r,int k){
    if(lazy[k]){
        lazy[k<<1]+=lazy[k];
        lazy[k<<1|1]+=lazy[k];
        int mid=(l+r)>>1;
        sum[k<<1]+=(w[mid]-w[l-1])*lazy[k];
        sum[k<<1|1]+=(w[r]-w[mid])*lazy[k];
        ci[k<<1]+=lazy[k];
        ci[k<<1|1]+=lazy[k];
        lazy[k]=0;
    }
}

void updataid(int l,int r,int k,int id,int val){
    if(l==r){
        sum[k]+=val;
        return ;
    }
    pushdown(l,r,k);
    int mid=(l+r)>>1;
    if(id<=mid) updataid(l,mid,k<<1,id,val);
    else updataid(mid+1,r,k<<1|1,id,val);
    pushup(k);
}

void updata(int l,int r,int k,int L,int R,int val){
    if(l>=L&&r<=R){
        sum[k]+=(w[r]-w[l-1])*1LL*val;
        ci[k]++;
        lazy[k]+=val;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(l,r,k);
    if(L<=mid) updata(l,mid,k<<1,L,R,val);
    if(R>mid) updata(mid+1,r,k<<1|1,L,R,val);
    pushup(k);
}


ll myfind(int l,int r,int k,ll x){
    if(l==r){
        if(l==1) return w[l];
        if(ci[k]*(w[l]-w[l-1])>=x){
            int z=x/ci[k];
            int h=x%ci[k];
            return w[l-1]+z+(h?1:0);
        }
        else{
            return w[l];
        }
    }
    int mid=(l+r)>>1;
    pushdown(l,r,k);
    if(sum[k<<1]>=x) return myfind(l,mid,k<<1,x);
    return myfind(mid+1,r,k<<1|1,x-sum[k<<1]);
}

int main(){
    int n;
    int a1,a2,b1,b2,c1,c2,m1,m2;
    int id=0;
    scanf("%d",&n);
    scanf("%d%d%d%d%d%d%d%d%d%d%d%d",&X[1],&X[2],&a1,&b1,&c1,&m1,&Y[1],&Y[2],&a2,&b2,&c2,&m2);
    L[1]=min(X[1],Y[1])+1;
    R[1]=max(X[1],Y[1])+1;
    L[2]=min(X[2],Y[2])+1;
    R[2]=max(X[2],Y[2])+1;
    w[++id]=L[1];
    w[++id]=L[2];
    w[++id]=R[1];
    w[++id]=R[2];
    for(int i=3;i<=n;++i){
        X[i]=(1LL*a1*X[i-1]+b1*1LL*X[i-2]+c1)%m1;
        Y[i]=(1LL*a2*Y[i-1]+b2*1LL*Y[i-2]+c2)%m2;
        L[i]=min(X[i],Y[i])+1;
        R[i]=max(X[i],Y[i])+1;
        w[++id]=L[i];
        w[++id]=R[i];
    }
    quchong(id);
    build(1,m,1);
    ll hh=0;
    for(int i=1;i<=n;++i){
        hh+=R[i]-L[i]+1;
        //cout<<L[i]<<" "<<R[i]<<" "<<hh<<endl;
        updataid(1,m,1,getid(L[i]),1);
        if(L[i]!=R[i])
            updata(1,m,1,getid(L[i]+1),getid(R[i]),1);
        printf("%lld\n",myfind(1,m,1,(hh+1)/2));
    }

    return 0;
}

当然还有另一种较为简洁的写法,对于区间直接写作左闭右开区间: [ l , r + 1 ] [l,r+1] [l,r+1],线段树端点维护区间。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=4e5+7;

int L[maxn],R[maxn];
int X[maxn],Y[maxn];

int m;

int w[maxn<<1];

void quchong(int n){
    sort(w+1,w+1+n);
    m=unique(w+1,w+1+n)-w-1;
}

int getid(int x){
    return lower_bound(w+1,w+1+m,x)-w;
}

ll lazy[maxn<<3|1];
ll sum[maxn<<3|1];

void build(int l,int r,int k){
    lazy[k]=sum[k]=0;
    if(l+1==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid,r,k<<1|1);
}

void pushup(int k){
    sum[k]=sum[k<<1]+sum[k<<1|1];
}

void pushdown(int l,int r,int k){
    if(l+1==r) return ;
    if(lazy[k]){
        lazy[k<<1]+=lazy[k];
        lazy[k<<1|1]+=lazy[k];
        int mid=(l+r)>>1;
        sum[k<<1]+=(w[mid]-w[l])*lazy[k];
        sum[k<<1|1]+=(w[r]-w[mid])*lazy[k];
        lazy[k]=0;
    }
}

void updata(int l,int r,int k,int L,int R,int val){
    if(l>=L&&r<=R){
        sum[k]+=(w[r]-w[l])*1LL*val;
        lazy[k]+=val;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(l,r,k);
    if(R<=mid) updata(l,mid,k<<1,L,R,val);
    else if(L>=mid) updata(mid,r,k<<1|1,L,R,val);
    else updata(l,mid,k<<1,L,R,val),updata(mid,r,k<<1|1,L,R,val);
    pushup(k);
}

ll myfind(int l,int r,int k,ll x){
    if(l+1==r){
        int len=w[r]-w[l];
        ll hh=sum[k]/len;
        return w[l]+x/hh-1+(x%hh?1:0);
    }
    int mid=(l+r)>>1;
    pushdown(l,r,k);
    if(sum[k<<1]>=x) return myfind(l,mid,k<<1,x);
    return myfind(mid,r,k<<1|1,x-sum[k<<1]);
}

int main(){
    int n;
    int a1,a2,b1,b2,c1,c2,m1,m2;
    int id=0;
    scanf("%d",&n);
    scanf("%d%d%d%d%d%d%d%d%d%d%d%d",&X[1],&X[2],&a1,&b1,&c1,&m1,&Y[1],&Y[2],&a2,&b2,&c2,&m2);
    L[1]=min(X[1],Y[1])+1;
    R[1]=max(X[1],Y[1])+1;
    L[2]=min(X[2],Y[2])+1;
    R[2]=max(X[2],Y[2])+1;
    ++R[1],++R[2];
    w[++id]=L[1];
    w[++id]=L[2];
    w[++id]=R[1];
    w[++id]=R[2];
    for(int i=3;i<=n;++i){
        X[i]=(1LL*a1*X[i-1]+b1*1LL*X[i-2]+c1)%m1;
        Y[i]=(1LL*a2*Y[i-1]+b2*1LL*Y[i-2]+c2)%m2;
        L[i]=min(X[i],Y[i])+1;
        R[i]=max(X[i],Y[i])+1;
        ++R[i];
        w[++id]=L[i];
        w[++id]=R[i];
    }
    quchong(id);
    //for(int i=1;i<=m;++i) cout<<w[i]<<" "; cout<<endl;
    build(1,m,1);
    ll hh=0;
    for(int i=1;i<=n;++i){
        //cout<<L[i]<<" "<<R[i]<<endl;
        hh+=R[i]-L[i];
        updata(1,m,1,getid(L[i]),getid(R[i]),1);
        printf("%lld\n",myfind(1,m,1,(hh+1)/2));
    }

    return 0;
}
/*
0 1 2 3 4 5 7 9 10
3 4
3
2 8
4
4 8
5
1 3
4
7 9
5
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值