Loading

【题解】[CEOI2020]权力药水

题意不是很复杂,只不过出题人强行把题面写的很长。

首先 \(D\) 很小,所以对于每个询问,我们可以直接找出两个人信任的人的集合,然后双指针扫一遍即可。

对询问离线,然后维护每个人的集合即可,这道题就做完了(

但是本题强制在线,把小清新模拟题强行变成毒瘤题。

我们需要记录,每个人,在每一时刻,信任的人的集合,这复杂度是 \(\mathcal{O}(NMD)\) 的,显然不行。

考虑对每个人动态开点,那么剩下的状态数是 \(\mathcal{O}(MD)\) 还是吃不消。

\(M\) 比较大,将时间作为动态开点的下标,那么对于一个关系 \((x,y)\),可以计算出它在图中存活的时间段,然后在线段树中区间加。

因为是动态开点线段树,所以我们需要标记永久化。最后在线段树上单点查询即可得到集合,排序后跑双指针得到答案。

时间复杂度 \(\mathcal{O}(M\log M+QD\log D)\)。另外本题卡空间,观察一下发现不少关系 \((x,y)\) 的存活时间都是从某个时间开始,一直持续到结束。对于这类关系,我们直接对每个点开一个变长数组维护即可。因为这类关系对每个点的贡献也不会超过 \(D\)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define ef 1000000000
using namespace std;
struct node {
    int l, r;
    vector<int>u;
} a[N * 55];
#define ls a[x].l
#define rs a[x].r
#define S a[x].u
int n, D, m, q, u[N], rt[N], idx;
void ins(int &x, int l, int r, int L, int R, int val) {
    if (!x)
        x = ++idx;

    if (L >= l && R <= r) {
        a[x].u.push_back(val);
        return ;
    }

    int mid = (L + R) >> 1;

    if (mid >= l)
        ins(ls, l, r, L, mid, val);

    if (mid < r)
        ins(rs, l, r, mid + 1, R, val);
}
vector<int>c[2];
void ask(int x, int L, int R, int pos, int op) {
    if (!x)
        return;

    for (int i = 0; i < (int)a[x].u.size(); i++)
        c[op].push_back(a[x].u[i]);

    if (L == R)
        return;

    int mid = (L + R) >> 1;

    if (mid >= pos)
        ask(ls, L, mid, pos, op);
    else
        ask(rs, mid + 1, R, pos, op);
}
vector<pair<int, int>>ad[N];
map<pair<int, int>, int>h;
inline void calc() {
    int p = c[0].size(), q = c[1].size();

    if (!p || !q) {
        cout << ef << endl;
        return;
    }

    sort(c[0].begin(), c[0].end());
    sort(c[1].begin(), c[1].end());
    int j = 0, mn = 0x7fffffff;
    rep(i, 0, q - 1) {
        while (j < p - 1 && c[0][j] <= c[1][i])
            j++;

        mn = min(mn, abs(c[0][j] - c[1][i]));

        if (j)
            mn = min(mn, abs(c[0][j - 1] - c[1][i]));
    }
    cout << mn << endl;
}
int main() {
    scanf("%d%d%d%d", &n, &D, &m, &q);
    rep(i, 1, n)scanf("%d", &u[i]);
    rep(i, 1, m) {
        int x, y;
        scanf("%d%d", &x, &y);
        x++, y++;

        if (x > y)
            swap(x, y);

        pair<int, int> cur = make_pair(x, y);

        if (h.count(cur)) {
            ins(rt[x], h[cur], i - 1, 1, m, u[y]);
            ins(rt[y], h[cur], i - 1, 1, m, u[x]);
            h.erase(h.find(cur));
        } else
            h.insert(make_pair(cur, i));
    }

    for (map<pair<int, int>, int>::iterator it = h.begin(); it != h.end(); it++) {
        int x = (*it).first.first, y = (*it).first.second;
        ad[x].push_back(make_pair(u[y], (*it).second));
        ad[y].push_back(make_pair(u[x], (*it).second));
    }

    while (q--) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        x++, y++;
        c[0].clear();
        c[1].clear();

        if (z) {
            ask(rt[x], 1, m, z, 0), ask(rt[y], 1, m, z, 1);

            for (int i = 0; i < (int)ad[x].size(); i++)
                if (ad[x][i].second <= z)
                    c[0].push_back(ad[x][i].first);

            for (int i = 0; i < (int)ad[y].size(); i++)
                if (ad[y][i].second <= z)
                    c[1].push_back(ad[y][i].first);

            calc();
        } else
            cout << ef << endl;
    }

    return 0;
}
posted @ 2021-06-21 22:52  7KByte  阅读(66)  评论(0编辑  收藏  举报