【洛谷P3247】最小公倍數
阿新 • • 發佈:2020-12-02
題目
題目連結:https://www.luogu.com.cn/problem/P3247
給定一張 \(N\) 個頂點 \(M\) 條邊的無向圖(頂點編號為 \(1,2,...,n\)),每條邊上帶有權值。所有權值都可以分解成 \(2^a\times 3^b\) 的形式。
現在有 \(q\) 個詢問,每次詢問給定四個引數 \(u,v,a,b\),請你求出是否存在一條頂點 \(u\) 到 \(v\) 之間的路徑,使得路徑依次經過的邊上的權值的最小公倍數為 \(2^a\times 3^b\)。
注意:路徑可以不是簡單路徑。
思路
問題等價於 \(u\) 與 \(v\) 所在連通塊內是否存在一張子連通圖滿足連線 \(u\)
將邊和詢問均按 \(a\) 從小到大排序,然後給邊分塊。定義第 \(j\) 個詢問“屬於”第 \(i\) 個塊當且僅當第 \(j\) 的詢問的 \(j\) 大於等於第 \(i-1\) 個塊最後一條邊的 \(a\) 且小於第 \(i\) 個塊最後一條邊的 \(a\)。
對於第 \(i\) 個塊,我們可以找出“屬於”它的詢問 \([l,r]\),然後將 \([l,r]\) 按照 \(b\) 排序,將前 \(i-1\) 個塊所有邊也將 \(b\) 排序。
此時顯然對於 \([l,r]\) 中任意一個詢問,它的 \(a\) 均不小於前 \(i-1\)
對於詢問 \(k\) “屬於”的塊 \(i\),我們直接列舉其中的所有邊,如果滿足這條邊的兩個權值分別不超過詢問的兩個權值,那麼就加進並查集。注意一個詢問結束後需要復原它所屬塊的貢獻。
時間複雜度 \(O(m\sqrt{n}\log n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=100010,M=320; int n,m,Q,T,L[M],R[M],father[N],dep[N],maxa[N],maxb[N],cpy[N][4]; bool ans[N]; queue<int> clr; struct edge { int u,v,a,b,id; }e[N],ask[N]; bool cmp1(edge x,edge y) { return x.a<y.a; } bool cmp2(edge x,edge y) { return x.b<y.b; } int find(int x) { return x==father[x]?x:find(father[x]); } void prework() { for (int i=1;i<=n;i++) { father[i]=i; dep[i]=1; maxa[i]=maxb[i]=-1; } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b); e[++m]=(edge){0,0,(int)2e9,(int)2e9,0}; scanf("%d",&Q); for (int i=1;i<=Q;i++) { scanf("%d%d%d%d",&ask[i].u,&ask[i].v,&ask[i].a,&ask[i].b); ask[i].id=i; } T=sqrt(m+log2(n))+1; for (int i=1;i<=T;i++) L[i]=R[i-1]+1,R[i]=min(m,i*T); sort(e+1,e+1+m,cmp1); sort(ask+1,ask+1+Q,cmp1); memset(cpy,-1,sizeof(cpy)); for (int i=1,r=0,l=1;i<=T;i++) { prework(); while (r<Q && ask[r+1].a<e[R[i]].a) r++; sort(e+1,e+1+R[i-1],cmp2); sort(ask+l,ask+1+r,cmp2); for (int k=1;l<=r;l++) { for (;k<=R[i-1] && e[k].b<=ask[l].b;k++) { int x=find(e[k].u),y=find(e[k].v); if (x==y) { maxa[x]=max(maxa[x],e[k].a); maxb[x]=max(maxb[x],e[k].b); continue; } if (dep[x]<dep[y]) { father[x]=y; dep[y]=max(dep[y],dep[x]+1); maxa[y]=max(e[k].a,max(maxa[x],maxa[y])); maxb[y]=max(e[k].b,max(maxb[x],maxb[y])); } else { father[y]=x; dep[x]=max(dep[x],dep[y]+1); maxa[x]=max(e[k].a,max(maxa[x],maxa[y])); maxb[x]=max(e[k].b,max(maxb[x],maxb[y])); } } for (int j=L[i];j<=R[i];j++) if (e[j].a<=ask[l].a && e[j].b<=ask[l].b) { int x=find(e[j].u),y=find(e[j].v); if (cpy[x][0]==-1) { cpy[x][0]=father[x]; cpy[x][1]=dep[x]; cpy[x][2]=maxa[x]; cpy[x][3]=maxb[x]; clr.push(x); } if (cpy[y][0]==-1) { cpy[y][0]=father[y]; cpy[y][1]=dep[y]; cpy[y][2]=maxa[y]; cpy[y][3]=maxb[y]; clr.push(y); } if (x==y) { maxa[x]=max(maxa[x],e[j].a); maxb[x]=max(maxb[x],e[j].b); continue; } if (dep[x]<dep[y]) { father[x]=y; dep[y]=max(dep[y],dep[x]+1); maxa[y]=max(e[j].a,max(maxa[x],maxa[y])); maxb[y]=max(e[j].b,max(maxb[x],maxb[y])); } else { father[y]=x; dep[x]=max(dep[x],dep[y]+1); maxa[x]=max(e[j].a,max(maxa[x],maxa[y])); maxb[x]=max(e[j].b,max(maxb[x],maxb[y])); } } int x=find(ask[l].u),y=find(ask[l].v); if (x==y && maxa[x]==ask[l].a && maxb[x]==ask[l].b) ans[ask[l].id]=1; while (clr.size()) { int x=clr.front(); clr.pop(); father[x]=cpy[x][0]; dep[x]=cpy[x][1]; maxa[x]=cpy[x][2]; maxb[x]=cpy[x][3]; cpy[x][0]=cpy[x][1]=cpy[x][2]=cpy[x][3]=-1; } } } for (int i=1;i<=Q;i++) if (ans[i]) printf("Yes\n"); else printf("No\n"); return 0; }