2021美团杯A.数据结构

补题:2021美团杯A.数据结构
比赛一开始看到不同数个数,张口主席树求区间不同数个数瞬间带歪队友,自己也在错误的道路上越走越远。
在中后期重新阅读题面发现每次询问的是全局不同数的个数,想到了最多只有\(n+1\)个数,对于每一次询问,我去计算有多少数会被删除,有多少数会被增加。无奈题刷少了,没想到统计不出现数的个数。
思路
彩蛋:学习了邓老师的代码,结果在看他代码的时候发现了一个手误情况,直接手造hack数据给了jls,成为了热心网友hack了邓老师,邓老师AK IOI,那么我hack邓老师,四舍五入就是我hack IOI(狗头保命)。然后发现我队也抽到了阳光普照奖2333333。
对于一个数\(x\),考虑其最左出现的位置\(l_x\),最右出现的位置\(r_x\),那么首先就确定每次询问的\(l,r\)满足\(l\leq l_x, r_x \leq r\)。然后考虑\(x-1\)的出现位置,若在\(l_x \leq pos \leq r_x\)中出现,那么\(x\)永远存在,其它情况就是会出现\(pos_t \leq l_x \leq r_x \leq pos_{t+1}\)。然后到这一步在赛中就不会维护了。
根据上面的这个不等式,我们发现当\(pos_t \leq l \leq l_x,r_x \leq r \leq pos_{t+1}\)时这个值会减去,那么只要用类似差分的思想,在\(r_x\)上设置\([pos_t+1,l_x]\)这一段\(+1\),在\(pos_{t+1}\)上对\([pos_t+1,l_x]\)这一段\(-1\)。这样就对右边的这一块进行了维护。
在输入完查询之后,我们从第一个位置出发,对于当前需要更新的位置\(i\),我们可以知道对于某个数我们在\([l,r]\)之间对其有一个更新,那么就把左边的这个\([l,r]\)更新到树状数组中,表示当我的右区间下标为\(i\)时,此时左区间\([l,r]\)有一个数会删除/增加。然后对于每一个询问查询一下当前右区间为\(i\)时,有多少个值在左区间不出现。
然后对于原始数组没有出现过的数字,那么直接把\(x-1\)的每一个不相交的区间更新进去即可。
代码
代码中注释掉的是邓老师的写法。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
#define x first
#define y second
const int N = 1e6 + 10, M = 13331;
const double PI = acos(-1.0);
const double eps = 1e-5;
const int mod = 1000000007;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
#define gcd __gcd

vector<int> v[N];
int l[N], r[N];
struct Node {
    int l, r, v;
};
vector<Node> vec[N];
int tr[N], res[N];
int n, m;
vector<PII> que[N];
int a[N];

int lowbit(int x) {
    return x & -x;
}

void add(int p, int x) {
    for(int i = p; i <= n; i += lowbit(i)) {
        tr[i] += x;
    }
}

int query(int x) {
    int res = 0;
    for(int i = x; i > 0; i -= lowbit(i)) {
        res += tr[i];
    }
    return res;
}

void update(int l1, int r1, int l2, int r2, int val) {
    if(l1 > r1 || l2 > r2) return;
    // printf("val = %d %d %d %d %d\n", val, l1, r1, l2, r2);
    vec[l2].push_back({l1, r1, 1});
    vec[r2 + 1].push_back({l1, r1, -1});
}

void solve() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i <= n + 1; i++) {
        v[i].push_back(0);
        l[i] = n + 1;
        r[i] = 0;
    }
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        l[a[i]] = min(l[a[i]], i);
        r[a[i]] = max(r[a[i]], i);
        v[a[i]].push_back(i);
    }
    for(int i = 0; i <= n + 1; i++) {
        v[i].push_back(n + 1);
    }

    for(int i = 1; i <= n + 1; i++) {
        // 不存在
        if(l[i] == n + 1) {
            for(int j = 0; j < v[i - 1].size() - 1; j++) {
                int l = v[i - 1][j] + 1, r = v[i - 1][j + 1] - 1;
                update(l, r, l, r, i);
            }
        }
        else {
            int L = 0, R = n + 1;
            bool flag = false;
            for(auto x : v[i - 1]) {
                if(l[i] <= x && x <= r[i]) {
                    flag = true;
                    break;
                }
                if(x < l[i]) L = max(x, L);
                if(x > r[i]) R = min(x, R);
            }
            if(flag) continue;
            update(L + 1, l[i], r[i], R - 1, i);
        }
    }

    // for(int i = 1; i <= n + 1; i++) {
    //     int l = n, r = 1;
    //     for(auto x : v[i]) {
    //         l = min(l, x);
    //         r = max(r, x);
    //     }
    //     vector<int> tmp; tmp.push_back(0);
    //     for(auto x : v[i - 1]) tmp.push_back(x);
    //     tmp.push_back(n + 1);
    //     for(int j = 0; j < tmp.size() - 1; j++) {
    //         int L = tmp[j], R = tmp[j + 1];
    //         // printf("i = %d, l = %d, r = %d, L = %d, R = %d\n", i, l, r, L, R);
    //         // printf("i = %d, %d %d %d %d\n", L + 1, min(l, R - 1), max(R, L + 1), R - 1);
    //         update(L + 1, min(l, R - 1), max(r, L + 1), R - 1, i);
    //     }
    // }
    for(int i = 1; i <= m; i++) {
        int l, r;
        scanf("%d%d", &l, &r);
        que[r].push_back({l, i});
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j < vec[i].size(); j++) {
            int l = vec[i][j].l, r = vec[i][j].r, x = vec[i][j].v;
            // printf("l = %d, r = %d\n", l, r);
            add(l, x);
            add(r + 1, -x);
        }
        for(int j = 0; j < que[i].size(); j++) {
            int x = que[i][j].x, id = que[i][j].y;
            res[id] = n + 1 - query(x);
        }
    }
    for(int i = 1; i <= m; i++) {
        printf("%d\n", res[i]);
    }
}

int main() {
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    #endif // ONLINE_JUDGE

    // int t; cin >> t; while(t--)
    solve();
    return 0;
}
posted @ 2021-07-14 00:54  这知识他不进我的脑子  阅读(121)  评论(0编辑  收藏  举报