LOJ#3145. 「APIO2019」桥梁 分块+可撤销并查集
看到这道题时没有什么思路,只会打暴力,而且数据范围比较有迷惑性,那基本就是分块了.
现在有两个暴力:
1.每次 $O(1)$ 更新边权,然后 $O(m)$ 暴力查询一个点的答案.
2.每次将所有边排序,然后 $O(1)/O(\log n)$ 查询点权
上述两种做法中查询与更新的时间复杂度很不平衡,所以考虑对操作进行分块来维持平衡.
令每一个块的大小为 $B$,将 $B$ 中所有询问按照限制重量由大到小排序.
对于所有未出现在 $B$ 中的边也按照边权从大到小排序.
假设 $B$ 中没有修改的话我们可以在 $O(\frac{m}{B} m)$ 的复杂度解决该问题.
而 $B$ 中的修改我们就暴力更新就行,平均下来一个询问要修改 $O(B)$ 次.
那么时间复杂度就是 $O(\frac{m}{B}m \log m+Q B \log m)$.
这个 $B$ 取大一点效果比较好,这里取到了 $700.$
#include <cstdio> #include <stack> #include <vector> #include <cstring> #include <algorithm> #define N 100004 #define M 200009 #define ll long long #define pb push_back #define setIO(s) freopen(s".in","r",stdin) using namespace std; const int B=800; int n,m,Q; int id[N],ans[M],mark[M],vis[M]; struct Edge { int u,v,c; Edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){} bool operator<(const Edge b) const { return c>b.c; } }e[M],f[M]; struct OPT { int ty,x,y,ti; OPT(int ty=0,int x=0,int y=0,int ti=0):ty(ty),x(x),y(y),ti(ti){} // 优先处理限重更大的 bool operator<(const OPT b) const { return y>b.y; } }a[M],g[M]; struct UFS { int size[N],p[N]; void init() { for(int i=1;i<N;++i) { size[i]=1,p[i]=i; } } int find(int x) { return p[x]==x?x:find(p[x]); } void ADD(int x,int y) { x=find(x),y=find(y); // 合并 x 与 y if(x==y) { return; } if(size[x]>size[y]) { swap(x,y); } // 将 x 合并到 y 上 size[y]+=size[x],p[x]=y; } struct DEL { int x,y; DEL(int x=0,int y=0):x(x),y(y){} }; stack<DEL>v; void TEM(int x,int y) { x=find(x),y=find(y); if(x==y) return; if(size[x]>size[y]) { swap(x,y); } size[y]+=size[x],p[x]=y; v.push(DEL(x,y)); } int Query(int x) { x=find(x); return size[x]; } void CLR() { while(!v.empty()) { DEL cur=v.top(); v.pop(); size[cur.y]-=size[cur.x]; p[cur.x]=cur.x; } } }T; void calc(int cur) { // 当前位置为 cur int cnt=0,c2=0; for(int i=cur;i<=Q;++i) { if(id[i]!=id[cur]) break; if(a[i].ty==1) mark[a[i].x]=cur; else g[++c2]=a[i],g[c2].ti=i; } for(int i=1;i<=m;++i) { vis[i]=0; if(mark[i]==cur) continue; f[++cnt]=e[i]; } sort(g+1,g+1+c2); sort(f+1,f+1+cnt); T.init(); for(int i=1,j=1;i<=c2;++i) { while(j<=cnt&&f[j].c>=g[i].y) { T.ADD(f[j].u,f[j].v); ++j; } for(int k=cur;k<=Q;++k) { if(id[k]!=id[cur]) break; if(a[k].ty==1) vis[a[k].x]=0; } for(int k=g[i].ti-1;k>=cur;--k) { if(a[k].ty==1&&!vis[a[k].x]&&a[k].y>=g[i].y) { vis[a[k].x]=1; T.TEM(e[a[k].x].u,e[a[k].x].v); } if(a[k].ty==1) vis[a[k].x]=1; } for(int k=g[i].ti+1;k<=Q;++k) { if(id[k]!=id[cur]) break; if(a[k].ty==1&&!vis[a[k].x]&&e[a[k].x].c>=g[i].y) { T.TEM(e[a[k].x].u,e[a[k].x].v); } } // 处理完贡献了,可以查询 ans[g[i].ti]=T.Query(g[i].x); T.CLR(); } for(int i=cur;i<=Q;++i) { if(id[i]!=id[cur]) break; if(a[i].ty==1) { e[a[i].x].c=a[i].y; } } } int main() { // setIO("input"); int x,y,z; scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c); } scanf("%d",&Q); // ty=1, w(x)->y // ty=2, s(x)->y for(int i=1;i<=Q;++i) { id[i]=(i-1)/B+1; scanf("%d%d%d",&a[i].ty,&a[i].x,&a[i].y); } for(int i=1;i<=Q;++i) { if(id[i]!=id[i-1]) { calc(i); } } for(int i=1;i<=Q;++i) { if(a[i].ty==2) printf("%d\n",ans[i]); } return 0; }