#分块,可撤销并查集#洛谷 3247 [HNOI2016]最小公倍数


题目


分析

考虑将询问和边权按 \(a\) 分别从小到大排序,考虑最暴力的做法就是将不超过 \(a'\) 且 不超过 \(b'\) 的边抽取出来

放进并查集判断 \(a,b\) 的最大值都能达到 \(a',b'\),并查集可以撤销,这样就做到 \(O(qm\log n)\)

考虑让边尽量减小抽取的次数,将边每 \(B\) 个分一段,那么询问实际上也会分段,

之前段的所有边可以用归并排序按照 \(b\) 排序,用双指针加入并查集。

当前段的边只有 \(B\) 条,每次完成一个询问后撤销操作,

\(O(qB\log n+\frac{m^2}{B}\log n)\),理论上取 \(B=\sqrt{\frac{m^2}{q}}\) 时最优,

实际上取 \(B=\sqrt{m\log{n}}\) 就可以跑得飞快了


代码

#include 
#include 
#include  
#include 
#define rr register
using namespace std;
const int N=50011,M=100011;
struct four{int x,y,a,b;}e[M],E[M],q[N];
int b[N],f[N],dep[N],mxa[N],mxb[N],yea[M],yeb[M],yed[M],yex[M],yey[M],Top,n,m,Q,block,rk[N],ans[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
bool cmp1(four x,four y){return x.ab?a:b;}
inline signed getf(int u){return f[u]==u?u:getf(f[u]);}
inline void uni(four t){
	rr int fa=getf(t.x),fb=getf(t.y);
	if (dep[fa]m)?(m+1):(i+block);
		for (rr int j=1;j<=Q;++j)
		if (e[i].a<=q[rk[j]].a&&(i+block>m||q[rk[j]].a

相关