这是HNOI2016的DAY1T1;
是一道众多CJ神犇口中的水题,也是YMD 用分块,莫队打天下的第一站;
这个题只要数学没有跪烂,应该还是可以看出来,
目标是要判定是否存在路径使x,y联通,且路上的a的最大值等于A,b的最大值等于B;
好我们先考虑暴力怎么解决;
对于前二十分,我们考虑对于每组询问,只加入a<=A且b<=B的边,在用并查集判断是否联通以及最大值是否为A和B;
好的,那么我们该怎么做呢;
首先我们身在HN,那么我们的脑海中一定要装着两个算法:莫队和CDQ,这是HN的骄傲!!
其次我们要谨记网管的教导“暴力出奇迹”,
“这题你不会,那你打个暴力就可以了嘛,暴力就可以AC嘛”;
谨记王队长的传奇“暴力进省队”;
所以这题的正解就是比纯暴力好一点点大暴力:分块!!!
思想和暴力及其类似:我们把边按照a sort,再进行分块,再把询问按照 b 进行排序;
1.我们对于每个块,先把前i-1个块中的边全部搞出来,再把对于满足这个块的a(即大于等于这个块的开头,小于等于这个块的结尾)的询问全部全部搞出来。并把搞出来的边按照b进行排序。
2.然后再枚举这些满足a的询问,有两步操作,一个是算这个块之前的贡献,一个是算这个块对当前询问的贡献;
3.第一步,因为前面已经按a排序了,所以前面的这些边的a值是一定满足询问的a的限制的,所以我们只要把已经按照b排了序的边判断是否满足询问的b的条件再依次加入
4.第二步,因为a和b都不一定满足要求,所以我们需要暴力对这个块中的边进行两次判断,即a和b都需要判断,再加入这条边;
5.并且由于满足这个块的询问的a并不一定是升序的,所以可能对于满足的两个询问i,j;
bi<bj,但ai>aj;这样就会有一个尴尬的问题,一条边的ax可能满足aj<ax<ai;显然这一条边在处理j的时候是不能算的,所以我们需要我们的并查集拥有回溯功能,即把刚刚加入的边删掉;
6.这样这个题目就可以AC了;
7.最后记得常数优化,cmp要打 const &,不然会gi烂;
// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#define RG register
using namespace std;
const int N=100050;
int gi()
{
RG int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
int n,m,fa[N],maxa[N],maxb[N],size[N],tot2,tot,ans[N];
int find(RG int x){return fa[x]==x?x:find(fa[x]);}
struct ac
{
int x,y,a,b,id;
}e[N],query[N],old[N];
struct AC
{
int x,y,f,ma,mb,size;
}add[N];
bool cmpa(const ac &a,const ac &b)
{
if(a.a==b.a) return a.b<b.b;
return a.a<b.a;
}
bool cmpb(const ac &a,const ac &b)
{
if(a.b==b.b) return a.a<b.a;
return a.b<b.b;
}
void merge(int x,int y,int a,int b)
{
x=find(x),y=find(y);
if(size[x]>size[y]) swap(x,y);
add[++tot2]=(AC){x,y,fa[x],maxa[y],maxb[y],size[y]};
if(x==y) {maxa[y]=max(maxa[y],a),maxb[y]=max(maxb[y],b);return;}
fa[x]=y;size[y]+=size[x];
maxa[y]=max(maxa[x],max(maxa[y],a));
maxb[y]=max(maxb[x],max(maxb[y],b));
}
void del()
{
for(RG int i=tot2;i;i--)
{
RG int x=add[i].x,y=add[i].y;
fa[x]=add[i].f,maxa[y]=add[i].ma,maxb[y]=add[i].mb,size[y]=add[i].size;
}
tot2=0;
}
int main()
{
n=gi(),m=gi();
RG int block=(int)sqrt(m);
for(RG int i=1;i<=m;i++) e[i].x=gi(),e[i].y=gi(),e[i].a=gi(),e[i].b=gi(),e[i].id=i;
sort(e+1,e+1+m,cmpa);
RG int T=gi();
for(RG int i=1;i<=T;i++) query[i].x=gi(),query[i].y=gi(),query[i].a=gi(),query[i].b=gi(),query[i].id=i;
sort(query+1,query+1+T,cmpb);
for(RG int i=1;i<=m;i+=block)
{
tot=0;
for(RG int j=1;j<=T;j++)
{
if(query[j].a>=e[i].a&&(i+block>m||query[j].a<e[i+block].a))
{
old[++tot]=query[j];
}
}
sort(e+1,e+1+i,cmpb);
for(RG int j=1;j<=n;j++) fa[j]=j,maxa[j]=maxb[j]=-1,size[j]=1;
RG int r=1;
for(RG int j=1;j<=tot;j++)
{
for(;r<i&&e[r].b<=old[j].b;r++)
{
merge(e[r].x,e[r].y,e[r].a,e[r].b);
}
tot2=0;
for(RG int l=i;l<i+block&&l<=m;l++)
if(e[l].a<=old[j].a&&e[l].b<=old[j].b)
{
merge(e[l].x,e[l].y,e[l].a,e[l].b);
}
RG int x=find(old[j].x),y=find(old[j].y);
if(x==y&&maxa[x]==old[j].a&&maxb[x]==old[j].b) ans[old[j].id]=1;
else ans[old[j].id]=0;
del();
}
}
for(RG int i=1;i<=T;i++)
{
if(ans[i]==1) puts("Yes");
else puts("No");
}
return 0;
}