最小公倍数
解读题意
判定有无路径包含u,v,且路径上的max{a[]}=A ,max{b[]}=B
注意
最大值的初值不能设成0,必须小于0。
切分
P20暴力:O(q,m)
并查集里存par,mxa,mxb
对于每个询问 q
每次清空并查集
把小于等于a且小于等于b的边并起来(边指到点上)
观察u,v是否在一个集合里
观察集合mxa是否等于a
观察集合mxb是否等于b
如果同时满足则存在这种路径
P40链:O(q*log(n)^2)
建一颗线段树,能够查询区间最大值(maxA,maxB)
对于每个询问q
先查找区间[u,v]最大值是否小于等于A,B
在向左右查找能否包含A,B(二分查找+区间查询)
考虑离线(可以让a,b有序)
P a=0: 并查集均摊O(n+m)+排序O(q+qlogq)
并查集存par和mx(并查集中最大的b)
由于a一直等于0,那么按b排序(从小到大)
不清空并查集
把小于等于b的边并起来(边随点并)
查询u,v是否在一个集合里
查询这个集合的mx是否等于b
pa,b<=30:
离线处理,按b从小到大排序。
开31个并查集
不清空并查集
把b<=B的边加入a<=A的每个并查集(每次加边更新最多31个并查集)
在[A]并查集里查询u,v是否在一个集合里
在[A]并查集查询这个集合的mxa,mxb是否等于A,B
正解流程
1.离线处理
2.分块+并查集+并查集回溯+并查集归并
3.把边按a排序,分成
m−−√
m
块
4.把询问按a排序,依次放进对应的(边)所属的块中,询问优先属于后面的块
5.把每个块中的边和询问按b排序
6.对每块依次处理
7.处理当前块时,把前面块放到一起按b排序
8.对当前块中的询问按b依次回答,先把符合b<=B的边加入并查集(不包含当前块)。
9.把前面块的边加入后,把当前块中符合a<=A&&b<=B的边加入并查集(这时要用栈记录并查集的更新,完成询问后撤销)
10.只记录和撤销当前块中的边的加入
总复杂度:排序(qlogq+mlogm)+并查集的加入和撤销(q* m−−√ m )+所有边的加入(m* m−−√ m )
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=50005;
//无向图,边有权值(a,b)
//多询问:判定有无:路径max{a[]}=a ,max{b[]}=b
int n,m,q,S,top;
bool ans[M];
struct Query {
int u,v,a,b,id;
} Qu[M],way[M*2],Qy;
vector<Query>Q[M];
bool cmp1(Query a,Query b) {
return a.a<b.a;
}
bool cmp2(Query a,Query b) {
return a.b<b.b;
}
struct node {
int t1,fa,t2,mxa,mxb,dep;
void rd(int a,int b,int c,int d,int e,int f) {
t1=a,fa=b,t2=c,mxa=d,mxb=e,dep=f;
}
} stk[M*2];
int fa[M],dep[M],mxa[M],mxb[M];
void init() {
top=0;
for(int i=1; i<=n; i++) {
fa[i]=i;
dep[i]=0;
mxa[i]=mxb[i]=-1;
}
}
int get_fa(int x) {
if(fa[x]==x)return x;
return get_fa(fa[x]);
}
void merge(int x,int y,int a,int b) {
int t1=get_fa(x);
int t2=get_fa(y);
if(dep[t1]>dep[t2])swap(t1,t2);
stk[++top].rd(t1,fa[t1],t2,mxa[t2],mxb[t2],dep[t2]);
fa[t1]=t2;
mxa[t2]=max(mxa[t2],max(mxa[t1],a));
mxb[t2]=max(mxb[t2],max(mxb[t1],b));
if(t1!=t2&&dep[t1]==dep[t2])dep[t2]++;
}
void recover(node K) {
int t1=K.t1,t2=K.t2;
fa[t1]=K.fa;
dep[t2]=K.dep;
mxa[t2]=K.mxa;
mxb[t2]=K.mxb;
}
int main() {
scanf("%d %d",&n,&m);
S=sqrt(m);
for(int i=0; i<m; i++) {
scanf("%d %d %d %d",&way[i].u,&way[i].v,&way[i].a,&way[i].b);
}
sort(way,way+m,cmp1);
scanf("%d",&q);
for(int i=1; i<=q; i++) {
scanf("%d %d %d %d",&Qu[i].u,&Qu[i].v,&Qu[i].a,&Qu[i].b);
Qu[i].id=i;
}
sort(Qu+1,Qu+1+q,cmp1);
int nw=0;
for(int i=1; i<=q; i++) {
while(nw+1<m&&way[nw+1].a<=Qu[i].a)nw++;
Q[nw/S].push_back(Qu[i]);
}
for(int i=0; i<m; i+=S) {
sort(way+i,way+min(i+S,m),cmp2);
}
for(int i=0; i<m; i+=S) {
if(Q[i/S].size()>0) {
sort(Q[i/S].begin(),Q[i/S].end(),cmp2);
}
}
for(int i=0; i<m; i+=S) {
sort(way,way+i,cmp2);
int nw=0;
init();
for(int j=0; j<Q[i/S].size(); j++) {
Qy=Q[i/S][j];
while(nw<i&&way[nw].b<=Qy.b) {
merge(way[nw].u,way[nw].v,way[nw].a,way[nw].b);
nw++;
}
int ntop=top;
for(int k=i; k<m&&k<i+S; k++) {
if(way[k].a<=Qy.a&&way[k].b<=Qy.b) {
merge(way[k].u,way[k].v,way[k].a,way[k].b);
}
}
int x=get_fa(Qy.u),y=get_fa(Qy.v);
if(x!=y||mxa[x]!=Qy.a||mxb[x]!=Qy.b)ans[Qy.id]=0;
else ans[Qy.id]=1;
while(top>ntop)recover(stk[top--]);
}
}
for(int i=1; i<=q; i++)printf("%s\n",ans[i]?"Yes":"No");
return 0;
}