[题解]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;
}

浙公网安备 33010602011771号