前置知识:可撤销并查集
用一个栈记录合并顺序,每次撤销将栈顶的元素恢复
但是这种方法不能路径压缩,因为会改变节点之间的关系,为了保证时间,可以按照 \(size\) 进行合并
题意显然为能不能找到一条路径,使这条路径上最大的 \(a\) 为 \(qa\),最大的 \(b\) 为 \(qb\)
因为有a和b两个限制,考虑按照a和b的大小分块
因为数的范围很大,所以可以按照先将边按a排序后的位置对应的值分块
对于每一段,记最小值为 \(mina\),最大值为\(maxa\)
先将满足 \(mina\le qa\le maxa\) 的询问加入s中
接下来考虑b的限制,因为此时a的问题已经解决了,所以可以对 \(a\le mina\)的边按b从小到大排序
为了便于边的插入和删除,可以先将询问按b从小到大排序
此时,依次枚举s中的询问,加入满足 \(a\le mina,b\le qb\) 的边,因为b是递增的,所以这些边加入后始终满足条件,不用删除
对于 \(mina\le qa\le maxa\) 的边,因为不一定满足条件,所以要利用可撤销并查集,对于每个询问,只加入符合条件的边,用完后撤销
此时,如果所求的u和v在同一个连通块,且块内最大的a和b均与所求相等,则答案为Yes,否则为No
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,q,blen,cnt,ans[50005];
struct node{int u,v,a,b,id;
}e[100005],qs[50005],s[50005];
bool cmp1(node x,node y)
{if(x.a==y.a) return x.b<y.b;return x.a<y.a;
}
bool cmp2(node x,node y)
{if(x.b==y.b) return x.a<y.a;return x.b<y.b;
}
int f[500005],siz[100005],maxa[100005],maxb[100005],top;
int find(int x)
{if(f[x]!=x) return find(f[x]);return x;
}
struct node1{int x,y,maxa,maxb,siz,fa;
}st[100005];
void merge(int u,int v,int a,int b)
{int x=find(u),y=find(v);if(siz[x]<siz[y]){swap(x,y),swap(u,v);}top++;st[top]=(node1){x,y,maxa[x],maxb[x],siz[x],f[y]};if(x==y){maxa[x]=max(maxa[x],a);maxb[x]=max(maxb[x],b);return;}f[y]=x;siz[x]+=siz[y];maxa[x]=max(maxa[x],max(maxa[y],a));maxb[x]=max(maxb[x],max(maxb[y],b));
}
void split()
{while(top){siz[st[top].x]=st[top].siz;maxa[st[top].x]=st[top].maxa;maxb[st[top].x]=st[top].maxb;f[st[top].y]=st[top].fa;top--;}
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v,a,b;scanf("%d%d%d%d",&u,&v,&a,&b);e[i]=(node){u,v,a,b,i};}scanf("%d",&q);for(int i=1;i<=q;i++){int u,v,a,b;scanf("%d%d%d%d",&u,&v,&a,&b);qs[i]=(node){u,v,a,b,i};}sort(e+1,e+m+1,cmp1);sort(qs+1,qs+q+1,cmp2);blen=sqrt(m);for(int i=1;i<=m;i+=blen){cnt=0;for(int j=1;j<=q;j++){if(qs[j].a>=e[i].a&&(i+blen>m||qs[j].a<e[i+blen].a)){s[++cnt]=qs[j];}}sort(e+1,e+i+1,cmp2);for(int j=1;j<=n;j++){f[j]=j,siz[j]=1;maxa[j]=maxb[j]=-1;}for(int j=1,k=1;j<=cnt;j++){while(k<i&&e[k].b<=s[j].b){merge(e[k].u,e[k].v,e[k].a,e[k].b);k++;}top=0;for(int l=i;l<i+blen&&l<=m;l++){if(e[l].a<=s[j].a&&e[l].b<=s[j].b){merge(e[l].u,e[l].v,e[l].a,e[l].b);}}int x=find(s[j].u),y=find(s[j].v);if(x==y&&maxa[x]==s[j].a&&maxb[x]==s[j].b) ans[s[j].id]=1;split();}}for(int i=1;i<=q;i++){if(ans[i]) printf("Yes\n");else printf("No\n");}return 0;
}