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