[题解]P9598 [JOI Open 2018] 山体滑坡 / Collapse

主播主播,你怎么调了一上午这个题,是不是可撤销并查集写了路径压缩啊 /yun

思路

为了方便,将题目中所有 0-index 改为 1-index。

切掉 \([p,p + 1]\) 等价于计算 \([1,p],[p + 1,n]\) 两个点集和其连边构成的连通块数之和。两个问题相互独立,现只考虑 \([1,p]\) 的计算方法。

对时间轴分块,设块长为 \(B\)\(L_i,R_i\) 分别表示块 \(i\) 的左右端点。考虑计算时间在块 \(i\) 中的询问,注意到此时在 \([1,i)\) 块中的操作一定会被使用,因此可以将所有 \([1,i)\) 块中的操作全部丢到并查集中。

需要注意的是,记 \(nxt_i\) 表示对于一个加边操作 \(i\),删除这条边的时间,特别的若没有删除这条边的操作,\(nxt_i = +\infty\)。若对于块 \(x\) 有一个操作 \(i\),那么当 \(L_x \leq nxt_i \leq R_x\) 时不能提前将这条边丢到并查集里面,因为对于一个询问不知道这个删除时间在其之前还是在其之后。我们记可以直接丢进并查集的边集为 \(E_1\),不可以直接丢进去的边集为 \(E_2\)

对于在块 \(x\) 中的询问按照 \(p\) 从小到大排序离线,对于在块 \(x\)\(E_1\) 边按照 \(\max(u,v)\) 排序。考虑双指针,将所有 \(E_1\)\(\max(u,v)\) 小于等于当前询问 \(p\) 的边全部加到并查集里面。

对于一个询问时间为 \(t\),则在块中满足条件的在 \(t\) 之前的边也应该加入并查集,但是这些边对于后面的询问不一定会加入所以需要可撤销并查集。时间复杂度 \(\Theta(\frac{n^2 \log n}{B} + nB)\),当 \(B = \sqrt{n \log n}\) 时最优,得复杂度 \(\Theta(n \sqrt{n \log n})\)

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second

using namespace std;

typedef pair<int,int> pii;
typedef pair<int,pii> pip;
const int N = 1e5 + 10;
const int inf = 1e9 + 10;
int n,m,q;
map<pii,int> lst;
vector<pii> Q[N];
int nxt[N],ans[N];
int tp,fp[N],sz[N],stk[N];

struct operation{
    int op,x,y;
}O[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

inline int find(int x){
    if (fp[x] == x) return fp[x];
    else return find(fp[x]);
}

inline int merge(int a,int b){
    int x = find(a),y = find(b);
    if (x == y) return 0;
    if (sz[x] < sz[y]) swap(x,y);
    sz[fp[stk[++tp] = y] = x] += sz[y];
    return 1;
}

inline void remove(){
    int y = stk[tp--];
    sz[fp[y]] -= sz[y];
    fp[y] = y;
}

inline void solve(){
    #define L(x) ((x - 1) * B + 1)
    #define R(x) (min(m,(x) * B))

    const int B = sqrt(m * 16);
    for (re int i = 1;L(i) <= m;i++){
        tp = 0;
        vector<pii> pv;
        vector<int> nv;
        vector<pip> qv;
        for (re int i = 1;i <= n;i++) sz[fp[i] = i] = 1;
        for (re int j = L(i);j <= R(i);j++){
            for (pii p:Q[j]) qv.push_back({p.snd,{j,p.fst}});
        }
        for (re int j = 1;j < L(i);j++){
            if (!O[j].op){
                if (nxt[j] > R(i)) pv.push_back({max(O[j].x,O[j].y),j});
                else if (L(i) <= nxt[j] && nxt[j] <= R(i)) nv.push_back(j);
            }
        } sort(pv.begin(),pv.end());
        sort(qv.begin(),qv.end(),[](const pip &a,const pip &b){ return a.snd.snd < b.snd.snd; });
        for (re int p = 0,q = 0,cnt = 0;p < qv.size();p++){
            int tot = 0;
            while (q < pv.size() && pv[q].fst <= qv[p].snd.snd) cnt += merge(O[pv[q].snd].x,O[pv[q].snd].y),q++;
            for (int x:nv){
                if (max(O[x].x,O[x].y) <= qv[p].snd.snd && nxt[x] > qv[p].snd.fst) tot += merge(O[x].x,O[x].y);
            }
            for (re int j = L(i);j <= qv[p].snd.fst;j++){
                if (!O[j].op){
                    if (max(O[j].x,O[j].y) <= qv[p].snd.snd && nxt[j] > qv[p].snd.fst) tot += merge(O[j].x,O[j].y);
                }
            } ans[qv[p].fst] += (qv[p].snd.snd - cnt - tot);
            while (tot--) remove();
        }
    }

    #undef L
    #undef R
}

int main(){
    n = read(),m = read(),q = read();
    fill(nxt,nxt + m + 1,inf);
    for (re int i = 1,op,x,y;i <= m;i++){
        op = read(),x = read(),y = read();
        if (x > y) swap(x,y);
        O[i] = {op,++x,++y};
        if (!op) lst[{x,y}] = i;
        else nxt[lst[{x,y}]] = i;
    }
    for (re int i = 1,a,b;i <= q;i++){
        a = read(),b = read();
        Q[++a].push_back({++b,i});
    } solve();
    for (re int i = 1;i <= m;i++) O[i] = {O[i].op,n - O[i].x + 1,n - O[i].y + 1};
    for (re int i = 1;i <= m;i++){
        for (re int j = 0;j < Q[i].size();j++) Q[i][j].fst = n - Q[i][j].fst;
    } solve();
    for (re int i = 1;i <= q;i++) printf("%d\n",ans[i]);
    return 0;
}
posted @ 2025-07-31 15:06  WBIKPS  阅读(17)  评论(0)    收藏  举报