2019杭电暑假多校10:Make Rounddog Happy【线段树】

题目:

2019杭电暑假多校10:Make Rounddog Happy

题意:

给定一个长度为N的数组,问有多少个子数组Array[L....R]满足max{Array[L....R]} - (R-L+1) <= K

分析:

这种求子段数目的题,一般都要套路的枚举一下一个端点,假设枚举左端点L,首先要知道最大R能取到多少,也就是说Array[L....R]内没有重复出现的数字,且R最大;这个可以用dp的思想从后往前O(N)预处理得到;然后R还得满足K+R-L+1-max{Array[L....R]} >= 0;只有R和前缀最大值未知;这个可以在线段树上处理出区间最大值,那怎么查询R的数量呢?线段树在递归的过程中先走左子树,再走右子树,走左子树的时候顺便取区间最大值,这不就得到前缀最大值了吗;设从L开始的前缀最大值为maxv,假设走到了线段树[xl,xr]这个区间,如果K+xl-L+1-maxv >= 0 ,那么[xl,xr]肯定都可以,如果K+xr-L+1-maxv < 0,那么[xl,xr]肯定都不可以,结束递归,否则继续递归下去

代码:

#include <bits/stdc++.h>

#define fi first
#define se second
#define pb push_back
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 3e5+35;
int T,n,k,a[maxn],pos[maxn],vis[maxn],MAX[maxn<<2];
inline int read() {
    int x = 0, f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    return x * f;
}
inline void write(LL x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
void BuildTree(int l,int r,int x){
    if(l == r){
        MAX[x] = a[r];
        return ;
    }
    int mid = (l+r)>>1;
    BuildTree(l,mid,x<<1);
    BuildTree(mid+1,r,x<<1|1);
    MAX[x] = max(MAX[x<<1],MAX[x<<1|1]);
}
LL ans; int maxv;
void QueryTree(int l,int r,int L,int R,int x,int v,int &maxv){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        if(k+l+1-v-max(maxv,MAX[x])>=0){
            ans += r-l+1;
            maxv = max(maxv,MAX[x]);
            return ;
        }
        if(l == r || k+r+1-v-maxv<0) {
            maxv = max(maxv,MAX[x]);
            return ;
        }
    }
    int mid = (l+r) >> 1;
    QueryTree(l,mid,L,R,x<<1,v,maxv);
    QueryTree(mid+1,r,L,R,x<<1|1,v,maxv);
}
LL solve(){
    ans = 0;
    for(int i = 1;i <= n; ++i){
        if(a[i]-1<=k) ans++;
        maxv = a[i];
        if(i+1<pos[i]) QueryTree(1,n,i+1,pos[i]-1,1,i,maxv);
    }
    return ans;
}
int main(){
    T = read();
    while(T--){
        n = read(), k = read();
        for(int i = 1;i <= n; ++i) a[i] = read(),vis[i] = n+1;
        pos[n] = n+1; vis[a[n]] = n;
        for(int i = n-1;i > 0; --i){           //预处理L=i时,R的最大值
            pos[i] = min(vis[a[i]],pos[i+1]);
            vis[a[i]] = i;
        }
        BuildTree(1,n,1);
        write(solve()),putchar('\n');
    }
    return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
boost::make_shared和std::make_shared都是用于创建智能指针的函数模板。它们的作用是在堆上创建一个对象,并返回一个指向该对象的智能指针。 boost::make_shared是Boost库中的函数模板,而std::make_shared是C++标准库中的函数模板。它们的功能和用法基本相同,都可以用来创建shared_ptr和weak_ptr。 使用make_shared函数模板可以避免显式地使用new操作符来创建对象,并且可以确保对象和引用计数的内存分配在一次内存分配中完成,从而提高了性能和内存利用率。 下面是使用boost::make_shared和std::make_shared的示例代码: ```cpp #include <iostream> #include <memory> #include <boost/make_shared.hpp> class MyClass { public: MyClass() { std::cout << "MyClass constructor" << std::endl; } ~MyClass() { std::cout << "MyClass destructor" << std::endl; } }; int main() { // 使用boost::make_shared创建shared_ptr boost::shared_ptr<MyClass> ptr1 = boost::make_shared<MyClass>(); // 使用std::make_shared创建shared_ptr std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>(); return 0; } ``` 在上面的示例中,我们分别使用boost::make_shared和std::make_shared创建了一个名为ptr1和ptr2的shared_ptr对象,它们都指向一个MyClass对象。当程序结束时,智能指针会自动释放内存,调用MyClass的析构函数。 需要注意的是,使用make_shared函数模板创建的智能指针对象不能使用delete操作符来释放内存,因为内存的释放是由智能指针自动管理的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值