hdu5289 Assignment --2015多校训练赛(一)

题意:

给定一串数字,里面存在多少个区间[l, r] 使得里面的最大值与最小值之差小于k。

思路:

用RMQ预处理出所有区间的最大值与最小值之差。之后枚举左端点L, 二分处理差值小于k的最左边端点R,把所有

的R-L+1加上就是答案。


/***********************************************
 * Author: fisty
 * Created Time: 2015-08-20 22:09:44
 * File Name   : hdu5289.cpp
 *********************************************** */
#include <iostream>
#include <cstring>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
#define Debug(x) cout << #x << " " << x <<endl
#define Memset(x, a) memset(x, a, sizeof(x))
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> P;
#define FOR(i, a, b) for(int i = a;i < b; i++)
#define lson l, m, k<<1
#define rson m+1, r, k<<1|1
#define MAX_N 100100
int t;   
int n, k;
int a[MAX_N];
int mi[MAX_N][20], ma[MAX_N][20];

void RMQ()
{
    for (int i = 0; i < n; ++i)
        mi[i][0] = ma[i][0] = a[i];
    for (int j = 1; (1<<j) <= n; ++j)
        for (int i = 0; i+(1<<j)-1 < n; ++i)
        {
            mi[i][j] = min(mi[i][j-1], mi[i+(1<<(j-1))][j-1]);
            ma[i][j] = max(ma[i][j-1], ma[i+(1<<(j-1))][j-1]);
        }

}

int query(int lt, int rt)
{
    int k = 0;
    while ((1<<(k+1)) <= rt-lt+1)
        k++;
    return max(ma[lt][k], ma[rt-(1<<k)+1][k]) - min(mi[lt][k], mi[rt-(1<<k)+1][k]);
}
int Binary_Search(int x){
    
    int l = x, r = n-1;
    while(r - l > 1){
        int mid = (l + r) / 2;
        if(query(x, mid) < k) l = mid;
        else r = mid;
    }
    if(query(x, r) < k)
        return r;
    return l;
}
void solve(){
    LL ans = 0;
    int t;
    for(int i = 0;i < n; i++){
        t = Binary_Search(i);
        ans += t - i + 1;
    }
    printf("%lld\n", ans);
}
int main() {
    //freopen("in.cpp", "r", stdin);
    //cin.tie(0);
    //ios::sync_with_stdio(false);
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &n, &k);
        for(int i = 0;i < n; i++){
            scanf("%d", &a[i]);
        }
        RMQ();
        solve();
    }
    return 0;
}

2.线段树:

首先,以a[n]起始的区间肯定是[n, n]。

然后以a[n-1]起始的区间最长是[n-1, n],然后考虑需不需要把区间的右值减小,也就是考虑a[n]和a[n-1]的差值是否小于k。假设最终的区间为[n-1, d(n-1)]。

于是对于以a[n-2]起始的区间,自然最长是[n-2, d(n-1)],然后考虑需不需要把区间的右值减小,也就是考虑这个区间内是否存在某个值与a[n-2]的差值大于等于k。

以此类推,以a[i]起始的区间应为[i, min(d(i+1), p)],其中p是i右侧最后一个满足与a[i]差值小于k的数的脚标。

采用线段树记录区间的最大值和最小值,就能查询出任意[i, n]区间里第一个满足与a[i]差值大于等于k的值的位置x,然后x-1即为最后一个满足与a[i]差值小于k的数的脚标。

/***********************************************
 * Author: fisty
 * Created Time: 2015-08-21 20:26:03
 * File Name   : hdu5289 (2).cpp
 *********************************************** */
#include <iostream>
#include <cstring>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
#define Debug(x) cout << #x << " " << x <<endl
#define Memset(x, a) memset(x, a, sizeof(x))
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> P;
#define FOR(i, a, b) for(int i = a;i < b; i++)
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
//线段树版 
//采用线段树记录区间的最大值和最小值,就能查询出任意[i, n]区间里第一个满足与
//Ai差值大于等于k的值的位置x,然后x-1即为最后一个满足与Ai差值小于k的数的脚标。
int n, k;
const int MAX_N = 110000;
int _max[MAX_N << 2];
int _min[MAX_N << 2];
int p = -1;
int a[MAX_N];
struct Tree{
    void pushup(int rt){
        _max[rt] = max(_max[rt<<1], _max[rt<<1|1]);
        _min[rt] = min(_min[rt<<1], _min[rt<<1|1]);
    }
    void build(int l, int r, int rt){
        _max[rt] = _min[rt] = 0;
        if(l == r){
            _min[rt] = _max[rt] = a[l];
            return ;
        }
        int m = (l + r) >> 1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void query(int a, int b, int v){
        query(a, b, 1, n, 1,  v);
    }
    void query(int a, int b, int l, int r, int rt, int v){
        if(l == r){
            if(abs(_min[rt] - v) >= k){
                if(p == -1 || p > l){
                    p = l;
                }
            }
            return;
        }
        int m = (l + r) >> 1;
        if(a <= m){
            if(abs(_min[rt<<1] - v) >= k || abs(_max[rt<<1] - v) >= k){
                query(a, b, lson, v);
            }
        }
        if(p == -1 && b > m){
            if(abs(_min[rt<<1|1] - v) >= k || abs(_max[rt<<1|1] - v) >= k){
                query(a, b, rson, v);
            }
        }
    }
}segtree;

int t;
void solve(){
    LL ans = 1;
    int d[MAX_N];
    d[n] = n;
    for(int i = n-1; i >= 1; i--){
        p = -1;
        segtree.query(i, n, a[i]);
        if(p == -1){
            p = n;
        }else{
            p--;
        }
        d[i] = min(d[i+1], p);
        ans += d[i] - i + 1;
    }   
    printf("%lld\n", ans);
}
int main() {
    //freopen("in.cpp", "r", stdin);
    //cin.tie(0);
    //ios::sync_with_stdio(false);
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &n, &k);
        for(int i = 1;i <= n; i++){
            scanf("%d", &a[i]);
        }
        segtree.build(1, n, 1);
        solve();
    }
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值