2021牛客寒假算法基础集训营3 E 买礼物 线段树+链表

https://ac.nowcoder.com/acm/contest/9983/E

目录

    • 题意
    • 分析
    • Code

题意

有n个礼物,每个礼物都有不同的编号,下面有m次操作

  1. x 拿走x位置的礼物
  2. x, y 询问[x,y]区间内是否存在两个一样的礼物

分析

看一眼单点修改区间查询,然后转换一下题意变成找区间出现最多的数

咦~这不是分块or莫队轻松解决

然后瞄一眼数据范围5e5,优雅暴力 基本是没戏

题解给了一种非常巧的解法,用链表链接前后两个编号相同的点,然后在查询区间中是否存在两个相同的编号就可以转化为[l, r]中有一个点的后继下标小于r,这样就完美符合要求

所以我们只要区间内最小值满足小于r即可,就是一个简单的rmq问题

至于链表的处理,两个数组前后各扫一遍即可

Code

#pragma GCC optimize(2)
#include 
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
const int N = 1e6 + 10;
const int M = 5e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MOD = 1e9+7;
typedef long long ll;
typedef pair<int, int> PII;
int a[N], vis[N], nxt[N], pre[N];
struct node {
     
    int l, r;
    int sum;
}t[N<<2];

void push_up(int u) {
     
    t[u].sum = min(t[u<<1].sum, t[u<<1|1].sum);
}
void build(int u, int l, int r) {
     
    t[u].l = l, t[u].r = r;
    if (l == r) {
     
        t[u].sum = nxt[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
    push_up(u);
}
void modify(int u, int pos, int val) {
     
    if (t[u].l == t[u].r) {
     
        t[u].sum = val;
        return;
    }
    int mid = (t[u].l + t[u].r) >> 1;
    if (pos <= mid) modify(u<<1, pos, val);
    else modify(u<<1|1, pos, val);
    push_up(u);
}
int query(int u, int ql, int qr) {
     
    if (ql <= t[u].l && qr >= t[u].r) return t[u].sum;
    int mid = (t[u].l + t[u].r) >> 1;
    int Min = INF;
    if (ql <= mid) Min = min(Min, query(u<<1, ql, qr));
    if (qr > mid) Min = min(Min, query(u<<1|1, ql, qr));
    return Min;
}
void solve() {
     
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++) nxt[i] = n+1, pre[i] = 0;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = n; i >= 1; i--) {
     
        if (vis[a[i]]) nxt[i] = vis[a[i]];
        vis[a[i]] = i;
    }
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++) {
     
        if (vis[a[i]]) pre[i] = vis[a[i]];
        vis[a[i]] = i;
    }
    build(1, 1, n);
    while (m--) {
     
        int opt, x, y; cin >> opt;
        if (opt == 1) {
     
            cin >> x;
            modify(1, x, INF);
            modify(1, pre[x], nxt[x]);
            nxt[pre[x]] = nxt[x]; //前继指向后继
            pre[nxt[x]] = pre[x]; //后继指向前继
        } else {
     
            cin >> x >> y;
            int tmp = query(1, x, y);
            if (tmp <= y) printf("1\n");
            else printf("0\n");
        }
    }

}

signed main() {
     
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
}


你可能感兴趣的:(思维,线段树)