hihocoder 1074 字体设计(线段树)

这题可以把问题转化为,对于一个位置,限制的位置必然是,递增时候,小于他本身,或者递减时候,大于他本身的位置,然后在这个区间中,寻找最大(小)值的位置,这样利用线段树维护即可,对于一个限制位置,可以先把数字离散化掉,然后用权值做节点很容易就处理出来了,然后第二个问题就是普通的rmq问题

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100005;

int n, a[N];
struct Hash {
    int v, id;
    void read(int i) {
        this->id = i;
        scanf("%d", &v);
    }
} h[N];

bool cmp(Hash a, Hash b) {
    return a.v < b.v;
}

#define lson(x) ((x<<1)+1)
#define rson(x) ((x<<1)+2)

struct Node {
    int l, r, Min, Max;
} node[N * 4];

void pushup(int x) {
    node[x].Min = min(node[lson(x)].Min, node[rson(x)].Min);
    node[x].Max = max(node[lson(x)].Max, node[rson(x)].Max);
}

void build(int l, int r, int x = 0) {
    node[x].l = l; node[x].r = r;
    if (l == r) {
        node[x].Min = n + 1;
        node[x].Max = 0;
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, lson(x));
    build(mid + 1, r, rson(x));
    pushup(x);
}

void add(int v, int val, int x = 0) {
    if (node[x].l == node[x].r) {
        node[x].Max = node[x].Min = val;
        return;
    }
    int mid = (node[x].l + node[x].r) / 2;
    if (v <= mid) add(v, val, lson(x));
    else add(v, val, rson(x));
    pushup(x);
}

int find(int l, int r, int x = 0) {
    if (node[x].l >= l && node[x].r <= r)
        return node[x].Min;
    int mid = (node[x].l + node[x].r) / 2;
    int ans = n + 1;
    if (l <= mid) ans = min(ans, find(l, r, lson(x)));
    if (r > mid) ans = min(ans, find(l, r, rson(x)));
    return ans;
}

int get(int l, int r, int bo, int x = 0) {
    if (node[x].l >= l && node[x].r <= r) {
        if (bo) return node[x].Max;
        return node[x].Min;
    }
    int ans;
    if (bo) ans = 0;
    else ans = n + 1;
    int mid = (node[x].l + node[x].r) / 2;
    if (l <= mid) {
        if (bo) ans = max(ans, get(l, r, bo, lson(x)));
        else ans = min(ans, get(l, r, bo, lson(x)));
    }
    if (r > mid) {
        if (bo) ans = max(ans, get(l, r, bo, rson(x)));
        else ans = min(ans, get(l, r, bo, rson(x)));
    }
    return ans;
}

int v[N], post[N];
int ans[N], an;

int main() {
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++)
            h[i].read(i);
        sort(h + 1, h + n + 1, cmp);
        for (int i = 1; i <= n; i++)
            a[h[i].id] = i;
        for (int i = 1; i <= n; i++)
            post[a[i]] = i;
        build(1, n);
        add(a[n], n);
        for (int i = n - 1; i >= 1; i--) {
            if (a[i + 1] < a[i]) v[i] = find(a[i] + 1, n);
            else v[i] = find(1, a[i] - 1);
            add(a[i], i);
        }
        for (int i = 1; i <= n; i++)
            add(i, a[i]);
        int u = 1;
        an = 0;
        while (u < n) {
            ans[an++] = u;
            u = post[get(u, v[u] - 1, a[u + 1] > a[u])];
        }
        ans[an++] = u;
        printf("%d\n", an);
        for (int i = 0; i < an; i++)
            printf("%d%c", ans[i], i == an - 1 ? '\n' : ' ');
    }
    return 0;
}
时间: 2024-09-29 03:04:25

hihocoder 1074 字体设计(线段树)的相关文章

hihocoder 1299 打折机票 线段树

题目链接:http://hihocoder.com/problemset/problem/1299 code: //线段树 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define maxn 100005 using namespace std; int price[maxn]; int Max[maxn]; int segTree[4*maxn

hihoCoder - 1079 - 离散化 (线段树 + 离散化)

#1079 : 离散化 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho在回国之后,重新过起了朝7晚5的学生生活,当然了,他们还是在一直学习着各种算法~ 这天小Hi和小Ho所在的学校举办社团文化节,各大社团都在宣传栏上贴起了海报,但是贴来贴去,有些海报就会被其他社团的海报所遮挡住.看到这个场景,小Hi便产生了这样的一个疑问--最后到底能有几张海报还能被看见呢? 于是小Ho肩负起了解决这个问题的责任:因为宣传栏和海报的高度都是一样的,所以宣传栏可以被视作

hihoCoder:#1079(线段树+离散化)

题目大意:给n个区间,有的区间可能覆盖掉其他区间,问没有完全被其他区间覆盖的区间有几个?区间依次给出,如果有两个区间完全一样,则视为后面的覆盖前面的. 题目分析:区间可能很长,所以要将其离散化.但离散化之后区间就变成了连续的,不再是离散的.也就是叶子由左右端点为u.u变成了左右端点为u-1.u,左右儿子有(l,mid)和(mid+1,r)变成了(l,mid)和(mid,r).所以离散化之后要以长度为1的区间为叶子节点建立线段树,而不是以1.2.3...为叶子节点建线段树. 代码如下: # inc

#1074 : 字体设计

描述 你正在协助某人开发某种新的 Linux 下的中文字体. 现在需要给上图里的黄点做「位置锚固」,然而有些黄点的位置可以由「插值(IUP)」确定,这样就可以减少锚固的数量. 例如,在上图中,#46 #49 #52 #53 的位置可以由 #42 和 #57 插值得到, 我就不需要去锚固它们,只需要固定 #42 和 #57 就可以了. 可以给这个字减少至少 12 字节 …… 抽象一下问题,现在给出输入数组 a, 定义 ax 可以被 al 和 ar 插值得到为: 存在 l < x < r 使得 a

hihoCoder - 1116 - 计算 (线段树)

计算 题目传送:#1116 : 计算 这里说下sum,pre,suf,ji数组的含义: sum:所求答案 pre:所有前缀积之和 suf:所有后缀积之和 ji:区间内所有数之积 以一个例子说明: 比如我们现在正在合并区间{3,4},{2,5} 而区间{3,4}所表示的 sum1=3 + 4 + 3 * 4 = 19 pre1 = 3 + 3 * 4 = 12 suf1 = 3 * 4 + 4 = 16 ji1 = 3 * 4 = 12 而区间{2,5} sum2 = 2 + 5 + 2 * 5

hihocoder 1080 线段树(区间更新)

题目链接:http://hihocoder.com/problemset/problem/1080 , 两种操作的线段树(区间更新). 这道题前一段时间一直卡着我,当时也是基础不扎实做不出来,今天又想了想其实还是比较简单的,也只能怪自己太弱了. 这道题坑就坑在是有两个操作:set和add,所以lazy标记数组就需要两个,但是有一点要考虑的是一个区间上set与add的先后关系——如果对于一个区间set和add标记同时存在,那么应该如果处理:一种情况set在add之前,那么就按照正常顺序来就可以了:

hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新

#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们可以化身上帝模式,买卖房产. 在这个游戏里,会不断的发生如下两种事件:一种是房屋自发的涨价或者降价,而另一种是政府有关部门针对房价的硬性调控.房价的变化自然影响到小Hi和小Ho的决策,所以他们希望能够知道任意时刻某个街道中所有房屋的房价总和是多少——但是很不幸的,游戏本身并不提供这样的计算.不过这难

hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)

#1078 : 线段树的区间修改 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于小Ho表现出的对线段树的理解,小Hi表示挺满意的,但是满意就够了么?于是小Hi将问题改了改,又出给了小Ho: 假设货架上从左到右摆放了N种商品,并且依次标号为1到N,其中标号为i的商品的价格为Pi.小Hi的每次操作分为两种可能,第一种是修改价格——小Hi给出一段区间[L, R]和一个新的价格NewP,所有标号在这段区间中的商品的价格都变成NewP.第二种操作是询问——小Hi给出一段

hihocoder 1586 ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛-题目9 : Minimum【线段树】

https://hihocoder.com/problemset/problem/1586 线段树操作,原来题并不难..... 要求乘积最小只要求区间内最大值.最小值和绝对值小的数,再判断min,max正负就行了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define lson l,