HDU 5289 Assignment

26 篇文章 0 订阅
24 篇文章 0 订阅

HDU 5289 Assignment

单调队列、线段树

题目

给一个数列,求满足下列条件的子区间的个数:
对于子区间[L, R]内的任意两个元素的差值小于k。

思路

暴力 O(nlognlogn) (967ms):
两个set维护,双指针ij。i指向区间首端元素,j指向区间尾端元素。set统计最值。如果最大值减最小值大于k了,那么在set中吧j指向的元素移除,j++。

单调栈 O(n) (358ms):
类似暴力,双指针,不过吧set优化成单调栈。单调栈ma维护单调递减的最大值,mi维护单调递增的最小值。这样栈里具有插入顺序与单调的两个性质。当最大值减最小值大于k了,判断栈头元素等不等于num[j],等于就移除首元素,然后增j。为了避免相等元素的情况,栈要严格增(减)。
每次处理完统计+=i-j。

线段树 O(nlogn) (421ms):
线段树做法比较耿直,枚举左端点l,查询右端点l(左端点右侧第一个大于k+num[l]或小于k+num[l]的r)。但是线段树的query不太好想。大体就是,用-1表示没结果,查询时遇到没结果或者有更优解(ans>l)时更新ans。查询时先查左儿子,如果有结果就不需要查右儿子了,因为左儿子的肯定更靠左。

int ans;
void queryR(int L, int R, LL num, int l, int r, int rt, LL k)
{
    if(l==r)
    {
        if(abs(stree[rt].ma-num)>=k||abs(stree[rt].mi-num)>=k)
        {
            if(ans==-1||ans>l)
                ans=l;
        }
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        if(abs(stree[rt<<1].mi-num)>=k||abs(stree[rt<<1].ma-num)>=k)
            queryR(L, R, num, lson, k);
    }
    if(ans==-1&&mid<R)
    {
        if(abs(stree[rt<<1|1].mi-num)>=k||abs(stree[rt<<1|1].ma-num)>=k)
            queryR(L, R, num, rson, k);
    }
}

另外,注意ans不可能大于上一次的ans,要记得取小。

还有类似线段树的RMQ做法,也是 O(nlogn)

代码

暴力 O(nlognlogn) (967ms):

#include <cstdio>
#include <set>
using namespace std;

typedef long long ll;
multiset<int> mi;
multiset<int,greater<int> > ma;
int a[100005];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        ma.clear();
        mi.clear();
        ll ans = 0;
        int i,j = 0;
        multiset<int>::iterator it1,it2;
        for(i = 0; i < n; i++){
            scanf("%d",&a[i]);
            ma.insert(a[i]);
            mi.insert(a[i]);
            while(j <= i && *ma.begin()-*mi.begin() >= k){
                it1 = ma.find(a[j]);
                it2 = mi.find(a[j]);
                ma.erase(it1);
                mi.erase(it2);
                j++;
            }
            ans += ma.size();
        }
        printf("%lld\n",ans);
    }
    return 0;
}

单调栈 O(n) (358ms):

#include<bits/stdc++.h>

#define M(a,b) memset(a,b,sizeof(a))
typedef long long LL;
using namespace std;
const int MAXN=100007;
deque<LL> ma, mi;
LL num[MAXN];
int main()
{
    int T;scanf("%d", &T);
    while(T--)
    {
        ma.clear(), mi.clear();
        int m, n;scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld", &num[i]);
        }
        int j=1;LL ans=0;
        for(int i=1;i<=n;i++)
        {
            while(!mi.empty()&&mi.back()>num[i]) mi.pop_back();
            mi.push_back(num[i]);
            while(!ma.empty()&&ma.back()<num[i]) ma.pop_back();
            ma.push_back(num[i]);

            while(!ma.empty()&&!mi.empty()&&ma.front()-mi.front()>=m)
            {
                ans+=(i-j);
                if(ma.front()==num[j]) ma.pop_front();
                if(mi.front()==num[j]) mi.pop_front();
                j++;
            }
        }
        while(j<=n) ans+=LL(n-j+1),j++;
        printf("%lld\n", ans);
    }
    return 0;
}

线段树 O(nlogn) (421ms):

#include<bits/stdc++.h>

#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long LL;
using namespace std;
const int MAXN=100007;
const int oo=0x3f3f3f3f;
LL num[MAXN];
struct Stree
{
    LL ma, mi;
}stree[MAXN<<2];
void pushup(int rt)
{
    stree[rt].ma=max(stree[rt<<1].ma, stree[rt<<1|1].ma);
    stree[rt].mi=min(stree[rt<<1].mi, stree[rt<<1|1].mi);
}
void build(int l, int r, int rt)
{
    if(l==r)
    {
        stree[rt].ma=stree[rt].mi=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lson), build(rson);
    pushup(rt);
}
int ans;
void queryR(int L, int R, LL num, int l, int r, int rt, LL k)
{
    if(l==r)
    {
        if(abs(stree[rt].ma-num)>=k||abs(stree[rt].mi-num)>=k)
        {
            if(ans==-1||ans>l)
                ans=l;
        }
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        if(abs(stree[rt<<1].mi-num)>=k||abs(stree[rt<<1].ma-num)>=k)
            queryR(L, R, num, lson, k);
    }
    if(ans==-1&&mid<R)
    {
        if(abs(stree[rt<<1|1].mi-num)>=k||abs(stree[rt<<1|1].ma-num)>=k)
            queryR(L, R, num, rson, k);
    }
}
int main()
{
    int T;scanf("%d", &T);
    while(T--)
    {
        int m, n;scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)
            scanf("%lld", &num[i]);
        build(1, n, 1);
        LL res=1;
        int last=n;
        for(int i=n-1;i>=1;i--)
        {
            ans=-1;
            queryR(i, n, num[i], 1, n, 1, m);
            if(ans==-1)
                ans=n;
            else 
                ans--;
            ans=min(ans, last);
            last=ans;
            res+=(ans-i+1);
        }
        printf("%lld\n", res);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值