1. 程式人生 > >BZOJ4537 HNOI2016最小公倍數(莫隊+並查集)

BZOJ4537 HNOI2016最小公倍數(莫隊+並查集)

  考慮邊只有一種權值的簡化情況。那麼當且僅當兩點可以通過邊權<=x的邊連通,且連通塊內最大邊權為x時,兩點間存在路徑max為x的路徑。可以發現兩種權值是類似的,當且僅當兩點可以通過邊權1<=x且邊權2<=y的邊連通,且連通塊內最大邊權1為x、最大邊權2為y時,兩點間存在路徑max為(x,y)的路徑。

  一種權值的情況很好處理,從小到大加邊並查集維護即可。觀察資料範圍容易想到根號演算法。考慮類似回滾莫隊的做法。按邊權1將邊分塊,塊內按邊權2排序。處理某塊時將所有權值1恰好小於該塊max的詢問找出來按邊權2排序,依次處理,每次加入之前塊中邊權2不大於它的邊,然後在塊內暴力找滿足條件的邊加入,使用帶撤銷並查集即可維護。

  注意存在u=v,a=b=0的詢問,所以集合內權值最大值的初值不能設成0。

  調了半天以為有什麼細節問題,結果發現並查集整個寫錯了,居然luogu還有80分資料水爆了啊。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
#define M 100010
#define
inf 1000000000 int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'
z')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int n,m,q,t,L[N],R[N],fa[N],mx[N],mxa[N],mxb[N],ans[N],cur[N],size[N],cnt; int top; struct data { int x,y,a,b,k,i; bool operator <(const data&t) const { return k<t.k||k==t.k&&b<t.b; } }e[M],a[N],undo[N]; int find(int x){return fa[x]==x?x:find(fa[x]);} int main() { #ifndef ONLINE_JUDGE freopen("bzoj4537.in","r",stdin); freopen("bzoj4537.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); int block=sqrt(m); for (int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read(),e[i].k=e[i].a; sort(e+1,e+m+1); for (int i=1;i<=m;i++) { e[i].k=(i-1)/block,mx[(i-1)/block]=max(mx[(i-1)/block],e[i].a),R[(i-1)/block]=max(R[(i-1)/block],i); if ((i-1)%block==0) L[(i-1)/block]=i; } cnt=(m-1)/block+1;mx[cnt]=inf+1;R[cnt]=-1; sort(e+1,e+m+1); q=read(); for (int i=1;i<=q;i++) { a[i].x=read(),a[i].y=read(),a[i].a=read(),a[i].b=read(),a[i].i=i; for (int j=0;j<=cnt;j++) if (a[i].a<mx[j]) {a[i].k=j;break;} } sort(a+1,a+q+1); int x=0; for (int i=0;i<=cnt;i++) { for (int i=1;i<=n;i++) fa[i]=i,size[i]=1; memset(mxa,255,sizeof(mxa)); memset(mxb,255,sizeof(mxb)); for (int j=0;j<i;j++) cur[j]=L[j]-1; while (x<q&&a[x+1].k==i) { x++; for (int j=0;j<i;j++) while (e[cur[j]+1].k==j&&e[cur[j]+1].b<=a[x].b) { cur[j]++; int p=find(e[cur[j]].x),q=find(e[cur[j]].y); if (size[p]<size[q]) swap(p,q); if (p!=q) size[p]+=size[q]; fa[q]=p;mxa[p]=max(max(mxa[p],mxa[q]),e[cur[j]].a),mxb[p]=max(max(mxb[p],mxb[q]),e[cur[j]].b); } int top=0; for (int j=L[i];j<=R[i];j++) { if (e[j].b>a[x].b) break; if (e[j].a<=a[x].a) { int p=find(e[j].x),q=find(e[j].y); if (size[p]<size[q]) swap(p,q),swap(e[j].x,e[j].y); top++;undo[top].x=q,undo[top].i=size[p]; undo[top].y=p,undo[top].a=mxa[p],undo[top].b=mxb[p]; if (p!=q) size[p]+=size[q]; fa[q]=p;mxa[p]=max(max(mxa[p],mxa[q]),e[j].a),mxb[p]=max(max(mxb[p],mxb[q]),e[j].b); } } if (find(a[x].x)==find(a[x].y)&&mxa[find(a[x].x)]==a[x].a&&mxb[find(a[x].x)]==a[x].b) ans[a[x].i]=1; for (;top;top--) { mxa[undo[top].y]=undo[top].a,mxb[undo[top].y]=undo[top].b; fa[undo[top].x]=undo[top].x;size[undo[top].y]=undo[top].i; } } } for (int i=1;i<=q;i++) printf(ans[i]?"Yes\n":"No\n"); return 0; }