【HNOI2016模拟4.10】 K小数查询

Description

维护一个长度为n的序列,使得其支持m次操作,包括区间插入和区间求k小数。
n,m<=80000,在任何时候|ai|<=5000000

Solution

一看到区间第k大/小,就想到了主席树。
但这个是区间修改!
怎么做呢?
(分块大法好)
观察到时限7s,果断上分块。(复杂度好不科学)
分块大法好?!
用每一个块维护排过序后的块和原来的块,然后对于每次询问,
二分答案?!
不科学的复杂度。
负数二分怎么打?
平移就好喽……

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 80005
#define M 400
#define inf 5000000
using namespace std;
struct note{int v,w;}a[N],b[N];
bool cmp(note x,note y) {return x.v<y.v;}
int n,m,q,bz,le,ri,k,u,v,lazy[M],l[M],r[M];
int find(int v,int x) {
    int le=l[v],ri=r[v]+1,mid;
    while (le<ri) {
        mid=(le+ri)/2;
        if (b[mid].v+lazy[v]<=x) le=mid+1;else ri=mid;
    }
    return le-l[v];
}
bool check(int x,int le,int ri) {
    int u,v,ans=0;
    fo(i,1,m) {
        if (l[i]<=le&&le<=r[i]) u=i;
        if (l[i]<=ri&&ri<=r[i]) v=i;
    }
    if (u==v) {
        fo(i,le,ri) if (a[i].v+lazy[u]<=x) ans++;
        if (ans>=k) return 1;else return 0;
    }
    fo(i,le,r[u]) if (a[i].v+lazy[u]<=x) ans++;
    fo(i,l[v],ri) if (a[i].v+lazy[v]<=x) ans++;
    fo(i,u+1,v-1) ans+=find(i,x);
    if (ans>=k) return 1;else return 0;
}
int main() {
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i].v),b[i].v=a[i].v,b[i].w=i;
    m=n/M+((n%M)>0);
    fo(i,1,m) {
        l[i]=r[i-1]+1;r[i]=min(l[i]+M,n);
        sort(b+l[i],b+r[i]+1,cmp);
    }
    fo(i,1,n) a[b[i].w].w=i;
    for(scanf("%d",&q);q;q--) {
        scanf("%d%d%d%d",&bz,&le,&ri,&k);
        if (bz==1) {
            fo(i,1,m) {
                if (l[i]<=le&&le<=r[i]) u=i;
                if (l[i]<=ri&&ri<=r[i]) v=i;
            }
            if (u==v) {
                fo(i,le,ri) a[i].v+=k,b[a[i].w].v+=k;
                sort(b+l[u],b+r[u]+1,cmp);
                fo(i,l[u],r[u]) a[b[i].w].w=i;
                continue;
            }
            fo(i,le,r[u]) a[i].v+=k,b[a[i].w].v+=k;
            sort(b+l[u],b+r[u]+1,cmp);
            fo(i,l[u],r[u]) a[b[i].w].w=i;
            fo(i,l[v],ri) a[i].v+=k,b[a[i].w].v+=k;
            sort(b+l[v],b+r[v]+1,cmp);
            fo(i,l[v],r[v]) a[b[i].w].w=i;
            fo(i,u+1,v-1) lazy[i]+=k;
        } else {
            u=1,v=inf*2;int mid;
            while (u<v) {
                mid=(u+v)/2;
                if (check(mid-inf,le,ri)) v=mid;else u=mid+1;
            }
            printf("%d\n",u-inf);
        }
    }
}
根据引用\[1\]和引用\[2\]的描述,题目的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值