P3247 [HNOI2016]最小公倍數
阿新 • • 發佈:2020-08-24
題目連結
題意分析
這道題的題意:給定一個無向圖,每一次詢問u與v之間是否存在一條路徑使得ai最大值是a,bi最大值是b
我一開始以為是離線+瓶頸樹
後來發現 這道題必須是恰好等於 而不是大於等於或者小於等於
我們看一下 這道題如果路徑上只有一個權值的話 我們可以使用雙指標
**把對於當前詢問ai 路徑中的ai小於等於這個ai的我們用帶權並查集維護 然後最大值是否等於就可以了 **
但是很不幸的是有兩個
無恥的參考了dalao的見解之後
對原始路徑按照ai進行排序 對詢問按照bi排序
對原始的邊來一個分塊
我們先列舉塊 並暴力列舉詢問 把ai在當前塊的先存起來
然後我們對於之前的塊按照bi排序
那麼加入的詢問以及排好序的一部分我們只需要考慮bi就可以了
因為現在我們使用的邊都是ai小於等於詢問的ai 所以只需要考慮ai最大值等於詢問的ai就可以了
由於只考慮bi並且都是有序的 我們使用雙指標就可以了
雙指標思路同上
對於當前塊內的我們暴力新增就可以了
但是要注意 由於詢問的ai還是無序的
所以新增的塊內的邊我們需要從並查集內刪除
也就是可撤銷並查集 利用棧就可以了
具體參見程式碼
CODE:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<string> #define N 100008 using namespace std; int n,m,q,S,top,tot,pos; int bel[N],cdy[N],wzy[N],deep[N],ans[N]; struct Edge { int u,v,a,b,id; }ed[N],qe[N],tmp[N]; struct Node { int x,y,deep,a,b; }sta[N]; bool cmp1(const Edge &A,const Edge &B) {return A.a==B.a ? A.b<B.b : A.a<B.a;} bool cmp2(const Edge &A,const Edge &B) {return A.b==B.b ? A.a<B.a : A.b<B.b;} int find(int x) {return bel[x]==x ? x : find(bel[x]);} void merge(Edge X) { int x=find(X.u),y=find(X.v); if(deep[x]<deep[y]) swap(x,y); sta[++top]=(Node){x,y,deep[x],cdy[x],wzy[x]};//入棧 cdy[x]=max(cdy[x],X.a);wzy[x]=max(wzy[x],X.b); if(x==y) return; bel[y]=x;deep[x]=max(deep[x],deep[y]+1); cdy[x]=max(cdy[x],cdy[y]); wzy[x]=max(wzy[x],wzy[y]); } bool query(int id) { int x=find(tmp[id].u),y=find(tmp[id].v); if(x!=y) return 0; if(cdy[x]!=tmp[id].a || wzy[x]!=tmp[id].b) return 0; return 1; } void del(int now) { int x=sta[now].x,y=sta[now].y; bel[y]=y;deep[x]=sta[now].deep; cdy[x]=sta[now].a;wzy[x]=sta[now].b; } int main() { cin>>n>>m;S=sqrt(m); for(int i=1;i<=m;++i) cin>>ed[i].u>>ed[i].v>>ed[i].a>>ed[i].b; cin>>q; for(int i=1;i<=q;++i) cin>>qe[i].u>>qe[i].v>>qe[i].a>>qe[i].b,qe[i].id=i; sort(ed+1,ed+m+1,cmp1);sort(qe+1,qe+q+1,cmp2); for(int i=1;i<=m;i+=S) { tot=0;pos=1; for(int j=1;j<=n;++j) bel[j]=j,cdy[j]=wzy[j]=-1,deep[j]=0; for(int j=1;j<=q;++j) if(qe[j].a>=ed[i].a && (i+S>m || qe[j].a<ed[i+S].a)) tmp[++tot]=qe[j]; if(tot==0) continue; if(i!=1) sort(ed+1,ed+i,cmp2);//前面的塊我們就可以雙指標了 for(int j=1;j<=tot;++j) { while(pos<i && ed[pos].b<=tmp[j].b) merge(ed[pos]),++pos;//雙指標維護 top=0;//top : 這裡是當前塊中的 不是普遍適用的 所以我們要入棧然後刪掉 for(int k=i;k<i+S&&k<=m;++k) if(ed[k].a<=tmp[j].a&&ed[k].b<=tmp[j].b) merge(ed[k]); ans[tmp[j].id]=query(j); while(top) del(top--); //刪棧 } } for(int i=1;i<=q;++i) if(ans[i]) puts("Yes"); else puts("No"); return 0; }