前置知识:可撤销并查集
用一个栈记录合并顺序,每次撤销将栈顶的元素恢复
但是这种方法不能路径压缩,因为会改变节点之间的关系,为了保证时间,可以按照 \(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;
}
标签:le,50005,公倍数,mina,maxa,qa,HNOI2016,include,P3247
From: https://www.cnblogs.com/wangsiqi2010916/p/18673500