题解:[APIO2019] 桥梁

前言

你说的对,但是联合省选 2025 D1T2。

这居然会成为我第一道操作分块。

思路分析

首先考虑无向图连通性问题基本不存在 polylog 做法,所以我们直接考虑操作分块。

从左往右扫每一个块,处理询问。这样,我们只需要考虑两类问题怎么做:

  • 未在本块修改的边,对本块内的询问产生的影响,要求 \(O(m)\)\(O(m \log m)\)

  • 在本块内修改的边,对本块内的询问产生的影响,要求 \(O(B^2)\)\(O(B^2 \log n)\)

第一个问题,我们考虑这样处理:

选出来所有未在本块修改的边,将它们按边权从大到小排序;选出来本块内所有的询问,将它们按重量从大到小排序,每次添加当前询问重量能走的边,用并查集维护联通块大小,复杂度 \(O(m \log m)\)

第二个问题,我们考虑这样处理:

枚举本块内所有的询问,枚举本块内所有的修改,考虑修改对询问的影响,也用并查集加边,复杂度 \(O(B^2 \log n)\)。因为我们对于每个询问加边不适用于下一个询问,所以我们需要撤销加的边,使用可撤销并查集即可。

现在来分析复杂度。复杂度为 \(O(\frac{qm \log m}{B}+B^3 \log n)\),取 \(B=\sqrt{q}\) 能做到 \(O(m\sqrt{q} \log m+q\sqrt{q}\log n)\)。卡卡应该能过,不想写归并了。

代码实现

稍微有一点点长,其实可以再精简。

#include<bits/stdc++.h>
using namespace std;
int n,m,q,op[100005],a[100005],b[100005],vis[100005],ans[100005],vis2[100005];
struct node{
    int x,y,w;
}h[100005];
vector<int> v1,v2,v3;
inline bool cmp1(int x,int y){
    return h[x].w>h[y].w;
}
inline bool cmp2(int x,int y){
    return b[x]>b[y];
}
int L[1005],R[1005],B,btot;
inline void build(){
    B=sqrt(q)*3;
    btot=q/B+(bool)(q%B);
    for(int i=1;i<=btot;i++){
        L[i]=(i-1)*B+1;
        R[i]=min(q,i*B);
    }
}
int fa[100005],size[100005];
inline void init(){
    for(int i=1;i<=n;i++){
        fa[i]=i;
        size[i]=1;
    }
}
struct line{
    int x,y,size,fa;
};
stack<line> s;
inline int find(int x){
    if(fa[x]==x) return x;
    else return find(fa[x]);
}
inline void merge(int x,int y){
    x=find(x);
    y=find(y);
    if(x==y) return;
    if(size[x]<size[y]) swap(x,y);
    s.push((line){x,y,size[x],fa[y]});
    fa[y]=x;
    size[x]+=size[y];
}
inline void undo(int now){
    while(now!=s.size()){
        line tmp=s.top();
        fa[tmp.y]=tmp.fa;
        size[tmp.x]=tmp.size;
        s.pop();
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>h[i].x>>h[i].y>>h[i].w;
    }
    cin>>q;
    for(int i=1;i<=q;i++){
        cin>>op[i]>>a[i]>>b[i];
    }
    init();
    build();
    for(int i=1;i<=btot;i++){
        undo(0);
        v1.clear();
        v2.clear();
        v3.clear();
        for(register int j=L[i];j<=R[i];j++){
            if(op[j]==1) vis[a[j]]=1,v3.push_back(a[j]);
            else v2.push_back(j);
        }
        for(register int j=1;j<=m;j++){
            if(!vis[j]) v1.push_back(j);
        }
        sort(v1.begin(),v1.end(),cmp1);
        sort(v2.begin(),v2.end(),cmp2);
        int now=0;
        for(register int j=0;j<v2.size();j++){
            while(now<v1.size() && h[v1[now]].w>=b[v2[j]]){
                merge(h[v1[now]].x,h[v1[now]].y);
                now++;
            }
            int tmp=s.size();
            for(register int l=v2[j];l>=L[i];l--){
                if(op[l]==1 && !vis2[a[l]] && b[l]>=b[v2[j]]){
                    merge(h[a[l]].x,h[a[l]].y);
                }
                if(op[l]==1) vis2[a[l]]=1;
            }
            for(register int l=0;l<v3.size();l++){
                if(!vis2[v3[l]] && h[v3[l]].w>=b[v2[j]]){
                    merge(h[v3[l]].x,h[v3[l]].y);
                }
            }
            for(register int l=L[i];l<=R[i];l++){
                vis2[a[l]]=0;
            }
            ans[v2[j]]=size[find(a[v2[j]])];
            undo(tmp);
        }
        for(register int j=L[i];j<=R[i];j++){
            if(op[j]==1){
                vis[a[j]]=0;
                h[a[j]].w=b[j];
            }
        }
    }
    for(register int i=1;i<=q;i++){
        if(op[i]==2) cout<<ans[i]<<'\n';
    }
    return 0;
}
posted @ 2025-04-11 19:53  _Kenma  阅读(10)  评论(0)    收藏  举报