zoukankan      html  css  js  c++  java
  • 【洛谷5443】[APIO2019] 桥梁(操作分块)

    点此看题面

    • 给定一张(n)个点(m)条边的无向图,每条边有一个边权。
    • (q)次操作,分为两种:修改一条边的边权;求从一个点出发只走边权小于等于(w)的边能到达多少个点。
    • (nle5 imes10^4,m,qle10^5)

    没有修改时的离线做法

    考虑没有修改的情况,直接按权值从大到小把所有的边和询问都排个序。

    然后按序枚举,则处理到一个询问时所有权值大于等于它的边都已被加入并查集,直接询问所在连通块大小即可。

    但现在有修改,肯定不能直接这样全局离线搞了。

    操作分块

    考虑把操作分块,设大小为(S)

    枚举到一个块时,我们已经处理好之前所有块的修改。

    而对于这个块,修改个数不超过(S)个,我们不妨直接记下所有会被修改到的特殊边,而剩余的边值都固定不变。

    仿照之前的做法,我们按权值从大到小把所有的固定边和询问都排个序。

    则处理到一个询问的时候,所有权值大于等于它的固定边都已被加入并查集,而会被修改到的特殊边最多只有(S)条,可以枚举每条特殊边判断边权是否大于等于它,若是则加入并查集中,处理完该询问后再撤销即可。

    复杂度分析为(O(frac qSmlogn+qSlogn)),理论上取(S=sqrt q)得到最优复杂度,但实际上取(S=sqrt{qlogn})似乎更快。。。

    代码:(O(qsqrt qlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define M 100000
    using namespace std;
    int n,m,ans[M+5];struct edge {int x,y,w;I bool operator < (Con edge& o) Con {return w>o.w;}}o[M+5],p[M+5];
    struct Q {int id,x,w;I bool operator < (Con Q& o) Con {return w>o.w;}}q[M+5];struct OP {int id,x,w,u;}z[M+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    namespace U//按秩合并可撤销并查集
    {
    	int f[N+5],g[N+5],T,Sx[M+5],Sy[M+5];I void Init() {for(RI i=1;i<=n;++i) g[f[i]=i]=1;}
    	I int fa(CI x) {return f[x]^x?fa(f[x]):x;}
    	I void Union(RI x,RI y,CI op=0)//合并,op=1表示要撤销
    	{
    		if((x=fa(x))==(y=fa(y))) return;g[x]<g[y]&&(swap(x,y),0),g[f[y]=x]+=g[y],op&&(Sx[++T]=x,Sy[T]=y);
    	}
    	I void Back() {W(T) g[Sx[T]]-=g[f[Sy[T]]=Sy[T]],--T;}//撤销
    }
    I void Solve(CI t1,CI t2)
    {
    	RI i,j,k,l;for(i=1;i<=m;++i) p[i]=o[i];for(k=1;k<=t1;++k) p[z[k].x].w=-1;//把可能会改变的边边权设为-1
    	for(U::Init(),sort(p+1,p+m+1),sort(q+1,q+t2+1),i=j=1;i<=t2;++i)
    	{
    		W(j<=m&&p[j].w>=q[i].w) U::Union(p[j].x,p[j].y),++j;//双指针,使所有边权大于等于当前询问权值的边都被加入
    		for(k=1;k<=t1&&z[k].id<q[i].id;++k) z[k].u=o[z[k].x].w,o[z[k].x].w=z[k].w;//枚举所有修改操作修改
    		for(l=1;l<=t1;++l) o[z[l].x].w>=q[i].w&&(U::Union(o[z[l].x].x,o[z[l].x].y,1),0);//枚举所有特殊边判断是否加入并查集
    		ans[q[i].id]=U::g[U::fa(q[i].x)],U::Back();for(--k;k;--k) o[z[k].x].w=z[k].u;//记录答案,然后撤销特殊边影响
    	}
    	for(k=1;k<=t1;++k) o[z[k].x].w=z[k].w;//修改
    }
    int main()
    {
    	RI Qt,sz,i;for(read(n,m),i=1;i<=m;++i) read(o[i].x,o[i].y,o[i].w);
    	RI op,x,y,t1=0,t2=0;for(read(Qt),sz=max((int)sqrt(Qt*log2(n)),1),i=1;i<=Qt;++i) read(op,x,y),
    		op==1?(z[++t1]=(OP){i,x,y},0):(q[++t2]=(Q){i,x,y},0),(!(i%sz)||i==Qt)&&(Solve(t1,t2),t1=t2=0);//每sz个操作一起做
    	for(i=1;i<=Qt;++i) ans[i]&&(writeln(ans[i]),0);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    ORACLE中dba,user,v$等开头的常用表和视图
    CentOS最基本的20个常用命令
    Spring IOC原理解读 面试必读
    Nginx Web服务应用
    Linux系统SSH免密登录
    Zabbix+Grafana打造高逼格监控系统
    Ansible入门
    你应该知道的 5 个 Docker 工具
    关于 Docker Hub 上不能注册 Docker ID 的问题
    Linux下终端录制工具-asciinema
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5443.html
Copyright © 2011-2022 走看看