#分塊,可撤銷並查集#洛谷 3247 [HNOI2016]最小公倍數
阿新 • • 發佈:2022-02-22
分塊,可撤銷並查集
時最優,
分析
考慮將詢問和邊權按 \(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; }