P3247 [HNOI2016] 最小公倍数 解题报告

前置知识:可撤销并查集

用一个栈记录合并顺序,每次撤销将栈顶的元素恢复

但是这种方法不能路径压缩,因为会改变节点之间的关系,为了保证时间,可以按照 \(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;
}
posted @ 2025-01-15 17:36  wangsiqi2010916  阅读(24)  评论(0)    收藏  举报