P3332 [ZJOI2013]K大数查询

题目描述

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

输入输出格式

输入格式:

第一行N,M接下来M行,每行形如1 a b c或2 a b c

输出格式:

输出每个询问的结果

输入输出样例

输入样例#1:
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
输出样例#1:
1
2
1

说明

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1

的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是

1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3

大的数是 1 。‍

N,M<=50000,N,M<=50000

a<=b<=N

1操作中abs(c)<=N

2操作中c<=Maxlongint

           算法一:区间线段树+权值线段树  

           算法二:整体二分+区间修改/询问树状数组

               整体二分部分正如上文,但是本题重点在于区间修改/询问树状数组

               1.单点修改+单点询问

                  普通树状数组即可

               2.区间修改+单点询问

                  考虑差分即可

                  我们兴建一个查分数组来做树状数组,并且将原有a数组保留  

                  修改时就修改delta数组即可

                  询问就加上delta的前缀和与a数组即可

               3.区间修改+区间询问

                  首先依旧是引入delta数组 delta[i]表示区间 [i, n] 的共同增量

                  于是修改区间 [l, r] 时修改 delta[l] 和 delta[r + 1] 即可(就是差分的思路)

                  查询的时候是查询区间 [l, r] 的和 即sum[r] - sum[l - 1] 所以现在的问题是求sum[i]

                  sum[i]=a[1]+....+a[i]+delta[1]*i+delta[2]*(i-1)...delta[i]*1

                      =sigma(a[x])+sigma(delta[x]*(i+1-x))

                      =sigma(a[x])+(i+1)*sigma(delta[x])-sigma(delta[x]*x)

                  于是维护两个树状数组delta[x]和delta[x]*x即可!

//luogu rank1 1049ms
//@2017-03-06 14:13
#include<cstdio>
#include<iostream>
#define ll long long 
#define EP if(ch==EOF) return EOF;
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();EP
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();EP}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int Z=6e4+5;
struct node{
    int x,y,k,opt,id;
}p[Z],p1[Z],p2[Z];
int n,m,cnt,ans[Z];
ll bit[Z],BIT[Z];
int mx,mn;
inline int lowbit(int x){
    return x&-x;
}
inline void update(int x,int d){
    ll k=(ll)x*d;
    for(int i=x;i<=n;i+=lowbit(i)) bit[i]+=d,BIT[i]+=k;
}
inline ll ask(int x){
    ll res=0;
    for(int i=x;i;i-=lowbit(i)) res+=bit[i];
    res*=x+1;
    for(int i=x;i;i-=lowbit(i)) res-=BIT[i];
    return res;
}
void solve(int l,int r,int x,int y){
    if(l>r||x>y) return ;
    if(x==y){
        for(int i=l;i<=r;i++) if(!p[i].opt) ans[p[i].id]=x;
        return ;
    }
    int mid=x+y>>1,t1(0),t2(0);
    ll tt(0);
    for(int i=l;i<=r;i++){
        if(p[i].opt){
            if(p[i].k<=mid){
                p1[++t1]=p[i];
                update(p[i].x,1);
                update(p[i].y+1,-1);
            }
            else{
                p2[++t2]=p[i];
            }
        }
        else{
            tt=ask(p[i].y)-ask(p[i].x-1);
            if(p[i].k<=tt){
                p1[++t1]=p[i];
            }
            else{
                p[i].k-=tt;
                p2[++t2]=p[i];
            }
        }
    }
    for(int i=1;i<=t1;i++) if(p1[i].opt) update(p1[i].x,-1),update(p1[i].y+1,1);
    for(int i=1;i<=t1;i++) p[l+i-1]=p1[i];
    for(int i=1;i<=t2;i++) p[l+t1+i-1]=p2[i];
    solve(l,l+t1-1,x,mid);
    solve(l+t1,r,mid+1,y);
}
int main(){
    n=read();m=read();
    for(int i=1;i<=m;i++){
        p[i].opt=read()&1;
        p[i].x=read();
        p[i].y=read();
        p[i].k=read();
        if(p[i].opt) p[i].k=n-p[i].k;
        else p[i].id=++cnt;
    }
    solve(1,m,0,n<<1);
    for(int i=1;i<=cnt;i++) printf("%d
",n-ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/6513079.html