1. 程式人生 > 其它 >#分塊,可撤銷並查集#洛谷 3247 [HNOI2016]最小公倍數

#分塊,可撤銷並查集#洛谷 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 <cstdio>
#include <cctype>
#include <algorithm> 
#include <cmath>
#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.a<y.a||(x.a==y.a&&x.b<y.b);}
bool cmp2(int x,int y){return q[x].b<q[y].b||(q[x].b==q[y].b&&q[x].a<q[y].a);}
bool cmp3(four x,four y){return x.b<y.b||(x.b==y.b&&x.a<y.a);}
inline void Clear(){for (rr int i=1;i<=n;++i) mxa[i]=mxb[i]=-1,f[i]=i,dep[i]=1;}
inline signed max(int a,int b){return a>b?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]<dep[fb]) fa^=fb,fb^=fa,fa^=fb;
	++Top,yex[Top]=fa,yey[Top]=fb,yea[Top]=mxa[fa],yeb[Top]=mxb[fa],yed[Top]=dep[fa];
	if (fa!=fb) f[fb]=fa,mxa[fa]=max(mxa[fa],mxa[fb]),mxb[fa]=max(mxb[fa],mxb[fb]);
	mxa[fa]=max(mxa[fa],t.a),mxb[fa]=max(mxb[fa],t.b);
	if (fa!=fb&&dep[fa]==dep[fb]) ++dep[fa];
}
inline void Trace(){
	for (rr int i=Top;i;--i)
		f[yey[i]]=yey[i],mxa[yex[i]]=yea[i],mxb[yex[i]]=yeb[i],dep[yex[i]]=yed[i];
}
inline void Merge(int l,int r){
	sort(e+l,e+r,cmp3);
	rr int lj=1,lk=l,tot=0;
	while (lj<l&&lk<r)
	if (e[lj].b<e[lk].b||(e[lj].b==e[lk].b&&e[lj].a<e[lk].b))
	    E[++tot]=e[lj++];
	    else E[++tot]=e[lk++];
	while (lj<l) E[++tot]=e[lj++];
	while (lk<r) E[++tot]=e[lk++];
	for (rr int i=1;i<=tot;++i) e[i]=E[i];
}
signed main(){
	n=iut(),m=iut(),block=sqrt(m*log2(n));
	for (rr int i=1;i<=m;++i)
		e[i]=(four){iut(),iut(),iut(),iut()};
	Q=iut();
	for (rr int i=1;i<=Q;++i)
	    q[i]=(four){iut(),iut(),iut(),iut()},rk[i]=i;
	sort(e+1,e+1+m,cmp1),sort(rk+1,rk+1+Q,cmp2);
	for (rr int i=1,nn,mn;i<=m;i+=block){
		Clear(),nn=0,mn=(i+block>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<e[i+block].a))
		    b[++nn]=rk[j];
		for (rr int j=1,o=1;j<=nn;++j){
			rr four t=q[b[j]];
			for (;o<i&&e[o].b<=t.b;++o) uni(e[o]); Top=0;
			for (rr int p=i;p<mn;++p)
			    if (e[p].a<=t.a&&e[p].b<=t.b) uni(e[p]);
			rr int fa=getf(t.x),fb=getf(t.y);
			ans[b[j]]=(fa==fb)&&(mxa[fa]==t.a)&&(mxb[fb]==t.b);
			Trace();
		}
        Merge(i,mn);
	}
	for (rr int i=1;i<=Q;++i) puts(ans[i]?"Yes":"No");
	return 0;
}