HRBUST 2388 - 千方残光剑(尺取法 + 权值线段树)

Description

有n个怪物排成一排,他们的生命值各不相同,asuka有一个叫做“千方残光剑”的技能,可以先秒杀掉y个怪物,然后对任意个怪物造成总量小于等于x点的伤害,但是如果不打死怪物就不会有经验,而asuka又有强迫症,他想保证自己最后杀掉的怪物是相邻的(是一整段的),那么释放一次技能最多能杀死多少个怪物呢?

Input

第一行输入一个t,t<=5代表数据组数。

对于每一组,第一行输入一个n,代表怪物个数。

第二行有n个数字,代表每个怪物的生命值,它们各不相同且ai<=n。

第三行一个整数x意义如题意。

第四行一个整数y意义如题意。

n<=100000。

Output

对于每一组输出一个整数x代表能够选取的怪物的最大数量。

Sample Input

1

5

1 2 3 4 5

7

1

Sample Output

4

Source

“科林明伦杯”计算机学院2018年程序设计竞赛


解题分析

选取最长的连续区间,使去掉最大的 y 个数字后的区间和不大于 x

尺取法(双指针 l , r )选择连续区间,不断右移 r 以更新区间;求去掉前 y 大数字后的区间和,可建一棵权值线段树(甚至不用离散化),维护区间数字个数 n u m 与区间和 s u m ,可在 l o g ( n ) 的时间找到第 y 大数的位置,此时左侧数字和即为所求。

若当前区间 [ l , r ] 去掉前 y 大数字后的区间和大于 x ,可使 l 右移,缩短区间至满足要求后,更新最大区间长度。

总时间复杂度 O ( n l o g ( n ) )


AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
const int MAXN = 100010;

LL x;
int n, y, a[MAXN];

template<typename T> inline void Read(T &x) {
    char ch = getchar(); x = 0;
    for (; ch<'0'; ch = getchar());
    for (; ch >= '0'; ch = getchar()) x = x * 10 + ch - '0';
}

struct STree{int l, r, num; LL sum;}st[MAXN << 2];
#define lson p << 1
#define rson p << 1 | 1
#define l(x) st[x].l
#define r(x) st[x].r
#define sum(x) st[x].sum
#define num(x) st[x].num

void build(int p, int l, int r)
{
    l(p) = l, r(p) = r;
    if(l == r){
        sum(p) = num(p) = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    sum(p) = sum(lson) + sum(rson);
    num(p) = num(lson) + num(rson);
}

void change(int p, int x, int d)
{
    if(l(p) == r(p)){
        sum(p) = d ? x : 0;
        num(p) = d;
        return;
    }
    int mid = (l(p) + r(p)) >> 1;
    if(x <= mid) change(lson, x, d);
    else change(rson, x, d);
    sum(p) = sum(lson) + sum(rson);
    num(p) = num(lson) + num(rson);
}

LL ask(int p, int k)
{
    if(k == num(p)) return 0;
    LL ans = 0;
    if(num(rson) > k) ans += sum(lson) + ask(rson, k);
    else if(num(rson) < k) ans += ask(lson, k - num(rson));
    else return sum(lson);
    return ans;
}


int main()
{
    int T; Read(T);
    while(T --){
        Read(n);
        for(int i = 1; i <= n; i ++) Read(a[i]);
        Read(x); Read(y);
        if(y >= n){
            cout << n << endl;
            continue;
        }
        build(1, 0, n);
        for(int i = 1; i <= y; i ++) change(1, a[i], 1);
        int l = 1, ans = y;
        for(int r = y + 1; r <= n; r ++){
            change(1, a[r], 1);
            while(x < ask(1, y)) change(1, a[l ++], 0);
            ans = max(ans, r - l + 1);
        }
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Hrbust_cx/article/details/80814206
今日推荐