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;
}

浙公网安备 33010602011771号