一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1058或洛谷1110 [ZJOI2007]报表统计

BZOJ原题链接

洛谷原题链接

STL

本题可以直接使用\(\mathtt{STL\ multiset}\)水过去。
因为本题插入数的操作实际上就是将原数列分为\(n\)段,在每一段的末尾插入数,所以我们只需维护每一段的开头和末尾两个数,这样更新相邻差值时只需考虑插入数与原末尾和下一段的开头两个数的差值就好。
而维护这个差值,只开一个\(\mathtt{multiset}\)就好(其中是所有相邻差值)。当插入一个数时,先将原本的末尾和后一段开头的差值从\(\mathtt{multiset}\)里删除,再插入新的差值即可。
而所有元素中最接近的两个元素的差值,实际上就是找每个数的前驱和后继,再作差取最小值。
同样开个\(\mathtt{multiset}\)来维护(其中是所有元素),当插入数时,找其前驱和后继作差取\(\min\)即可。

#include<cstdio>
#include<set>
using namespace std;
const int N = 5e5 + 10;
multiset<int> S, D;
int st[N], ed[N], mi = 1e9, n, l;
char C[20];
inline int re()
{
    int x = 0;
    char c = getchar();
    bool p = 0;
    for (; c < '0' || c > '9'; c = getchar())
        p |= c == '-';
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return p ? -x : x;
}
inline void re_l()
{
    char c = getchar();
    for (l = 0; (c < 'A' || c > 'Z') && c != '_'; c = getchar());
    for (; (c >= 'A' && c <= 'Z') || c == '_'; c = getchar())
        C[l++] = c;
}
inline int jd(int x) { return x < 0 ? -x : x; }
inline int minn(int x, int y) { return x < y ? x : y; }
inline void update_S(int x)
{
    multiset<int>::iterator it = S.lower_bound(x), k = it;
    mi = minn(mi, minn(jd(x - *it), jd(x - *(--k))));
    S.insert(x);
}
inline void update_D(int x, int y)
{
    if (x ^ n)
        D.erase(D.find(jd(st[x + 1] - ed[x]))), D.insert(jd(st[x + 1] - y));
    D.insert(jd(y - ed[x]));
    ed[x] = y;
}
int main()
{
    int i, m, x, y;
    n = re(); m = re();
    S.insert(-1e9); S.insert(1e9);
    for (i = 1; i <= n; i++)
        update_S(st[i] = ed[i] = re());
    for (i = 2; i <= n; i++)
        D.insert(jd(st[i] - ed[i - 1]));
    for (i = 1; i <= m; i++)
    {
        re_l();
        if (C[0] == 'I')
        {
            x = re(); y = re();
            update_S(y); update_D(x, y);
        }
        else
            if (C[4] == 'S')
                printf("%d\n", mi);
            else
                printf("%d\n", *D.begin());
    }
    return 0;
}

平衡树+线段树/堆/……

若不用\(\mathtt{STL}\),只需将其中一个\(\mathtt{multiset}\)改为平衡树,另一个改为线段树/堆之类的维护最小值的数据结构即可。
这里我用的是\(\mathtt{Splay}\)和线段树。
平衡树同样是插入所有元素,找前驱后继作差取最小值。
而线段树是插入每一段的末尾与其下一段的开头的差,维护一个最小值,而段内的相邻差值则再开一个变量维护最小值,对该询问的答案就是这两个取\(\min\)

#include<cstdio>
using namespace std;
const int N = 5e5 + 10;
struct sp {
    int so[2], fa, v;
};
sp tr[N << 1];
int MI[N << 2], st[N], ed[N], ro, SP, l;
char C[20];
inline int re()
{
    int x = 0;
    char c = getchar();
    bool p = 0;
    for (; c < '0' || c > '9'; c = getchar())
        p |= c == '-';
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return p ? -x : x;
}
inline void re_l()
{
    char c = getchar();
    for (l = 0; (c < 'A' || c > 'Z') && c != '_'; c = getchar());
    for (; (c >= 'A' && c <= 'Z') || c == '_'; c = getchar())
        C[l++] = c;
}
inline int jd(int x) { return x < 0 ? -x : x; }
inline int minn(int x, int y) { return x < y ? x : y; }
inline int maxn(int x, int y) { return x > y ? x : y; }
inline int who(int x) { return tr[tr[x].fa].so[0] ^ x ? 1 : 0; }
inline void ch(int x, int fa, int lr) { tr[fa].so[lr] = x; tr[x].fa = fa; }
inline void rtt(int x)
{
    int y = tr[x].fa, r = tr[y].fa, soy = who(x), sor = who(y);
    ch(tr[x].so[soy ^ 1], y, soy);
    ch(y, x, soy ^ 1); ch(x, r, sor);
}
void sy(int x, int y)
{
    int z = tr[x].fa;
    if (!(ro ^ y))
        ro = x;
    y = tr[y].fa;
    for (; tr[x].fa ^ y; z = tr[x].fa)
        if (!(tr[z].fa ^ y))
            rtt(x);
        else
        {
            who(x) ^ who(z) ? rtt(x) : rtt(z);
            rtt(x);
        }
}
inline int newnode(int x, int fa)
{
    tr[++SP].fa = fa;
    tr[SP].v = x;
    return SP;
}
void ins(int x)
{
    if (!ro)
        ro = newnode(x, 0);
    else
        for (int y, k = ro; ; k = tr[k].so[y])
        {
            if (!(tr[k].v ^ x))
                return;
            if (!tr[k].so[y = x < tr[k].v ? 0 : 1])
            {
                int nw = newnode(x, k);
                tr[k].so[y] = nw; sy(nw, ro);
                return;
            }
        }
}
inline int qu_pre(int x)
{
    int s = -1e9;
    for (int k = ro; k; k = tr[k].so[x <= tr[k].v ? 0 : 1])
        if (x >= tr[k].v)
            s = maxn(s, tr[k].v);
    return s;
}
inline int qu_suc(int x)
{
    int s = 1e9;
    for (int k = ro; k; k = tr[k].so[x >= tr[k].v ? 1 : 0])
        if (x <= tr[k].v)
            s = minn(s, tr[k].v);
    return s;
}
void pp(int r) { MI[r] = minn(MI[r << 1], MI[r << 1 | 1]); }
void bu(int r, int x, int y)
{
    if (!(x ^ y))
        MI[r] = jd(st[x] - st[x - 1]);
    else
    {
        int mid = (x + y) >> 1;
        bu(r << 1, x, mid);
        bu(r << 1 | 1, mid + 1, y);
        pp(r);
    }
}
void upd(int r, int x, int y, int q, int k)
{
    if (!(x ^ y))
        MI[r] = k;
    else
    {
        int mid = (x + y) >> 1;
        q <= mid ? upd(r << 1, x, mid, q, k) : upd(r << 1 | 1, mid + 1, y, q, k);
        pp(r);
    }
}
int main()
{
    int i, n, m, x, y, mi = 1e9, mis = 1e9;
    n = re(); m = re();
    st[0] = st[n + 1] = 1e9;
    for (i = 1; i <= n; i++)
    {
        st[i] = ed[i] = re();
        if (i ^ 1)
            mis = minn(mis, minn(jd(qu_pre(st[i]) - st[i]), jd(qu_suc(st[i]) - st[i])));
        ins(st[i]);
    }
    bu(1, 1, n);
    for (i = 1; i <= m; i++)
    {
        re_l();
        if (C[0] == 'I')
        {
            x = re(); y = re();
            mis = minn(mis, minn(jd(qu_pre(y) - y), jd(qu_suc(y) - y)));
            ins(y);
            mi = minn(mi, jd(y - ed[x]));
            upd(1, 1, n, x, jd(st[x + 1] - y));
            ed[x] = y;
        }
        else
            if (C[4] == 'S')
                printf("%d\n", mis);
            else
                printf("%d\n", minn(mi, MI[1]));
    }
    return 0;
}

posted on 2019-03-20 21:26  Iowa_Battleship  阅读(...)  评论(... 编辑 收藏

导航

统计