【2019南京网络赛:F】Greedy Sequence(set/线段树 + 思维)

题目地址:https://nanti.jisuanke.com/t/41303

题目:


简而言之,题目最终转化为求距a[i] 长度为k的范围内小于a[i]的最大值,然后递推求答案

 

解题思路:


方法1: set

遍历数组,动态得到以a[i]为中心的区间[i-k,i+k],用set查询这个区间内第一个大于等于a[i]的值,它的前一个就是小于a[i]的最大值(如果有的话),代码简单好写。

方法2:线段树

读入时记录i所在的下标pos[i],先建一个空线段树,i从1开始遍历到n,找到以i为中心的区间[pos[i]-k,pos[i]+k],查询区间最大值,得到的这个最大值一定是小于i的,且是以i为中心的区间内的最大值,就是我们要找的值,然后把i插入线段树中。

注意:区间的左右边界不要越界!

 

ac代码:


set:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+50;
int t,n,k,a[maxn],ans[maxn],pre[maxn];
set<int> s;
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        s.clear();
        scanf("%d %d", &n, &k);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        int l = 1, r = min(k+1, n);
        for(int i = 1; i <= r; i++)
            s.insert(a[i]);
        for(int i = 1; i <= n; i++)//a[i]左右两边小于a[i]的最大值
        {
            //扩右边
            while(r-i<k && r+1<=n) s.insert(a[++r]);
            //除左边
            while(i-l>k) s.erase(a[l++]);
            auto it = s.lower_bound(a[i]);
            if(it != s.begin()) pre[a[i]] = *(--it);
            else pre[a[i]] = 0;//没有找到比自己小的
        }
        ans[1] = 1, ans[0] = 0;
        for(int i = 2; i <= n; i++)
            ans[i] = ans[pre[i]] + 1;
        for(int i = 1; i <= n; i++)
        {
            if(i!=1) printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

线段树:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int val[maxn*4], a[maxn], pos[maxn], ans[maxn];
int t, n, k;
void push_up(int id)
{
    val[id] =  max(val[id<<1], val[id<<1|1]);
}
void build(int id, int l, int r)
{
    if(l == r)
    {
        val[id] = 0;
        return ;
    }
    int mid = (l+r) >> 1;
    build(id << 1, l, mid);
    build(id << 1 | 1, mid+1, r);
    push_up(id);
}
int query(int id, int l, int r, int x, int y)
{
    if(x <= l && r <= y)
        return val[id];
    int mid = (l+r) >> 1, ans = 0;
    if(x <= mid) ans = max(ans, query(id << 1, l, mid, x, y));
    if(y > mid) ans = max(ans, query(id << 1 | 1, mid+1, r, x, y));
    return ans;
}
void change(int id, int l, int r, int pos, int v)
{
    if(l == r)
    {
        val[id] = v;
        return ;
    }
    int mid = (l+r) >> 1;
    if(pos <= mid) change(id << 1, l, mid, pos, v);
    if(pos > mid) change(id << 1 | 1, mid+1, r, pos, v);
    push_up(id);
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d", &n, &k);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            pos[a[i]] = i;
            ans[i] = 0;
        }
        build(1, 1, n);
        for(int i = 1; i <= n; i++)
        {
            int l = max(1, pos[i]-k), r = min(n, pos[i]+k);
            int pre = query(1, 1, n, l, r);
            //cout << i << "  " << pre << endl;
            ans[i] = ans[pre] + 1;
            change(1, 1, n, pos[i], i);
        }
        for(int i = 1; i <= n; i++)
        {
            if(i!=1) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}

 

 

全部评论

相关推荐

勇敢的联想人前程似锦:如果我是你,身体素质好我会去参军,然后走士兵计划考研211只需要200多分。
点赞 评论 收藏
分享
牛客410815733号:这是什么电影查看图片
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务