题意简述
n
个点
数据范围
1≤n,q≤4×104
1≤m≤105
思路
一开始的思路是莫队,把ab看成xy坐标。
本质是查询以一个点为左上点的和坐标轴围成的矩形中,两个点的连通性,以及是否边界上有点。
这个可以用并查集维护。
虽然知道并查集不支持删除,但还是乱搞,无果…(可持久化?)
但是发现并查集支持撤销(操作序倒序删除)操作,我们只需要用一个栈来记录历史更改即可。
这样的话只能使用按秩合并,路径压缩我认为没法用,zyz大佬似乎说可以?(手动@zyz)
那么分块。
将边按照a排序后分块,每块大小为
B
,每块维护一个前缀并查集,维护使用前缀块的这些边点的连通性。并查集一开始为空,我们动态加边来维护。
将询问按b排序,按这个顺序处理询问。
每次询问,将b小于等于当前询问的边插入,因为维护前缀,我们插入到它的块和它后面的块即可。
然后二分找到最靠近询问的a的一个前缀,以这个并查集为基础,暴力插入它后面
在并查集中查询后撤销。
并查集还需要维护a,b的最大值。
复杂度
O(mlognmB+qlognB)
因为
n,m,q
同阶。
B
理论上去取
原因的话应该是
这告诉我们实践是检验真理的唯一标准…
以后遇到这种分块题造一造数据卡一卡块大小取最优值就好啦。
不过我好像做麻烦了呵呵呵呵
代码
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 50010
#define MAXE 100010
#define MAXQ 50010
#define block 1600
struct Ed{
int u,v,a,b;
Ed(int _u=0,int _v=0,int _a=0,int _b=0)
{
u=_u,v=_v,a=_a,b=_b;
}
bool operator < (const Ed &n1) const
{
return a<n1.a;
}
}E[MAXE];
bool cmp(const int &n1,const int &n2)
{
return E[n1].b<E[n2].b;
}
struct Qu{
int u,v,a,b,id;
bool operator < (const Qu &n1) const
{
return b<n1.b;
}
}Q[MAXQ];
int n,m,q,cur,pos,bp,lim;
int ord[MAXE];
bool ans[MAXQ];
int sta[MAXN],hismxa[MAXN],hismxb[MAXN],top;
int max(int a,int b,int c)
{
return max(max(a,b),c);
}
struct Dsu{
int fa[MAXN],rank[MAXN],mxa[MAXN],mxb[MAXN];
void init()
{
top=0;
for (int i=0;i<=n;i++)
{
fa[i]=i;
rank[i]=1;
mxa[i]=-1;
mxb[i]=-1;
}
}
int getfa(int x)
{
while (fa[x]!=x)
x=fa[x];
return x;
}
void unionn(int u,int v,int a,int b,bool ty)
{
int fu=getfa(u),fv=getfa(v);
if (fu==fv)
{
if (ty)
{
sta[top]=fu+MAXN;
hismxa[top]=mxa[fu];
hismxb[top]=mxb[fu];
top++;
}
mxa[fu]=max(mxa[fu],a);
mxb[fu]=max(mxb[fu],b);
}
else if (rank[fu]<rank[fv])
{
if (ty)
{
sta[top++]=fu;
sta[top]=fv+MAXN;
hismxa[top]=mxa[fv];
hismxb[top]=mxb[fv];
top++;
}
fa[fu]=fv;
mxa[fv]=max(mxa[fv],mxa[fu],a);
mxb[fv]=max(mxb[fv],mxb[fu],b);
}
else if (rank[fv]<rank[fu])
{
if (ty)
{
sta[top++]=fv;
sta[top]=fu+MAXN;
hismxa[top]=mxa[fu];
hismxb[top]=mxb[fu];
top++;
}
fa[fv]=fu;
mxa[fu]=max(mxa[fv],mxa[fu],a);
mxb[fu]=max(mxb[fv],mxb[fu],b);
}
else
{
if (ty)
{
sta[top++]=fv;
sta[top]=fu+MAXN;
hismxa[top]=mxa[fu];
hismxb[top]=mxb[fu];
top++;
sta[top++]=-fu;
}
fa[fv]=fu;
mxa[fu]=max(mxa[fv],mxa[fu],a);
mxb[fu]=max(mxb[fv],mxb[fu],b);
rank[fu]++;
}
}
void restore()
{
while (top)
{
top--;
if (sta[top]>MAXN)
{
mxa[sta[top]-MAXN]=hismxa[top];
mxb[sta[top]-MAXN]=hismxb[top];
}
else if (sta[top]>0)
fa[sta[top]]=sta[top];
else
rank[-sta[top]]--;
}
}
}D[70],zero;
int read()
{
char ch=getchar();
int ret=0;
for (;ch<'0'||ch>'9';ch=getchar());
for (;ch>='0'&&ch<='9';ret=ret*10+ch-'0',ch=getchar());
return ret;
}
void add_Ed(int x)
{
int bx=x/block;
for (int i=bx;i<=lim;i++)
D[i].unionn(E[x].u,E[x].v,E[x].a,E[x].b,0);
}
int main()
{
n=read(),m=read();
for (int i=0;i<m;i++)
E[i].u=read(),E[i].v=read(),E[i].a=read(),E[i].b=read();
sort(E,E+m);
for (int i=0;i<m;i++)
ord[i]=i;
sort(ord,ord+m,cmp);
q=read();
for (int i=0;i<q;i++)
{
Q[i].u=read(),Q[i].v=read(),Q[i].a=read(),Q[i].b=read();
Q[i].id=i;
}
sort(Q,Q+q);
zero.init();
lim=(m-1)/block;
for (int i=0;i<=lim;i++)
D[i].init();
cur=0;
for (int i=0;i<q;i++)
{
while (cur<m&&E[ord[cur]].b<=Q[i].b)
add_Ed(ord[cur++]);
pos=upper_bound(E,E+m,Ed(0,0,Q[i].a,0))-E-1;
bp=pos/block;
if (bp==0)
{
for (int j=block*bp;j<=pos;j++)
if (E[j].b<=Q[i].b)
zero.unionn(E[j].u,E[j].v,E[j].a,E[j].b,1);
int fu=zero.getfa(Q[i].u),fv=zero.getfa(Q[i].v);
if (fu!=fv||zero.mxa[fu]!=Q[i].a||zero.mxb[fu]!=Q[i].b) ans[Q[i].id]=0;
else ans[Q[i].id]=1;
zero.restore();
}
else
{
for (int j=block*bp;j<=pos;j++)
if (E[j].b<=Q[i].b)
D[bp-1].unionn(E[j].u,E[j].v,E[j].a,E[j].b,1);
int fu=D[bp-1].getfa(Q[i].u),fv=D[bp-1].getfa(Q[i].v);
if (fu!=fv||D[bp-1].mxa[fu]!=Q[i].a||D[bp-1].mxb[fu]!=Q[i].b) ans[Q[i].id]=0;
else ans[Q[i].id]=1;
D[bp-1].restore();
}
}
for (int i=0;i<q;i++)
puts(ans[i] ? "Yes" : "No");
return 0;
}