P3247 [HNOI2016]最小公倍数 题解

题意简述:

给定一个无向图,边权带两个值 \((a,b)\),给定 \(q\) 次询问,每次询问给定两个点,求两个点直接是否有 \(\max(a)=x\)\(\max(b)=y\) 的简单或非简单路径。

解:

如果是单次询问,可以想到我们把所有 \(a\le x\)\(b\le y\) 的边加入,判断 \((u,v)\) 是否联通且带有 \(a=x\) 和带有 \(b=y\) 的边是否能走到,这可以用并查集解决就行了。

现在问题在处理多次询问。

我们可以把每条边按 \(a\) 不下降排序,询问按 \(y\) 不下降排序。

我们再把边按数量分块,再从小到大枚举块,每次找出所有 \(x\le max_a\)\(x\ge min_a\)\(max_a\)\(min_a\) 是指块内的值,也就是找到所有在块内的询问。

那么对于在这个块前面的边 \(a\) 一定小于等于 \(x\),那么可以对前面的块中所有边按 \(b\) 排序,那么只需要找到 \(b\le y\) 的所有边即可,由于询问的 \(y\) 单调递增,那么枚举前面块的边时可以从上次继承过来。

然后处理块内元素,暴力枚举找合适的边加入即可。

为下一次询问,我们需要撤销所有块内操作,这个直接在修改时用栈维护一下修改的值,然后处理完询问复原即可。

那么块内操作不能路径压缩,否则撤回的复杂度会高达 \(O(n)\),每一次询问撤一次复杂度就为 \(O(n\times q)\)

最后一步,我们需要判断是否能走到带有 \(a=x\)\(b=y\) 的边,这个好处理,在并查集时记录一下当前集合的最大 \(a\) 与最大 \(b\) 即可。

时间复杂度:\(O(q\times\sqrt n\times\log(\sqrt n))\)

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=2e5+5;
int n,m,k,cnt,siz[N],p[N],ans[N],f[N],fa[N],fb[N];
struct node4
{
	int u,v,data,maxa,maxb;
}qp[N];
struct node
{
	int from,to,dat1,dat2;
}a[N];
int cmp(node fi,node se)
{
	return fi.dat1<se.dat1;
}
int cmp3(node fi,node se)
{
	return fi.dat2<se.dat2;
}
struct node2
{
	int name,u,v,dat1,dat2;
}b[N];
int cmp2(node2 fi,node2 se)
{
	return fi.dat2<se.dat2;
}
int bfind(int x)
{
	if(x==f[x])return x;
	return bfind(f[x]);
}
int afind(int x)
{
	if(x==f[x])return x;
	return f[x]=afind(f[x]);
}
int main()
{
	//freopen("eegcd.in","r",stdin);
	//freopen("eegcd.out","w",stdout);
	scanf("%d%d",&n,&m);
	k=sqrt(m);
	for(int i=1;i<=m;i++)scanf("%d%d%d%d",&a[i].from,&a[i].to,&a[i].dat1,&a[i].dat2);
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;i++)scanf("%d%d%d%d",&b[i].u,&b[i].v,&b[i].dat1,&b[i].dat2),b[i].name=i;
	sort(a+1,a+1+m,cmp);
	sort(b+1,b+1+q,cmp2);
	for(int i=1;i<=m;i++)p[i]=(i-1)/k+1;
	a[m+1].dat1=-1;
	for(int i=1;(i-1)*k+1<=m;i++)
	{
		int l=(i-1)*k+1,r=min(i*k,m),bef=0;
		for(int j=1;j<=n;j++)f[j]=j,fa[j]=fb[j]=-1;
		for(int j=1;j<=q;j++)
		{
			if(b[j].dat1>=a[l].dat1&&b[j].dat1<=a[r].dat1)
			{
				if(b[j].dat1==a[r].dat1&&a[r+1].dat1==b[j].dat1)continue;
				int cnt=0;
				for(int t=bef+1;t<=l-1&&a[t].dat2<=b[j].dat2;t++)
				{
					fa[afind(a[t].to)]=max(fa[afind(a[t].to)],max(fa[afind(a[t].from)],a[t].dat1));
					fb[afind(a[t].to)]=max(fb[afind(a[t].to)],max(fb[afind(a[t].from)],a[t].dat2));
					f[afind(a[t].from)]=afind(a[t].to);
					bef=t;
				}
				for(int t=l;t<=r&&a[t].dat1<=b[j].dat1;t++)
				{
					if(a[t].dat2<=b[j].dat2)
					{
						qp[++cnt]={bfind(a[t].from),bfind(a[t].to),f[bfind(a[t].from)],fa[bfind(a[t].to)],fb[bfind(a[t].to)]};
						fa[bfind(a[t].to)]=max(fa[bfind(a[t].to)],max(fa[bfind(a[t].from)],a[t].dat1));
						fb[bfind(a[t].to)]=max(fb[bfind(a[t].to)],max(fb[bfind(a[t].from)],a[t].dat2));
						f[bfind(a[t].from)]=f[bfind(a[t].to)];
					}
				}
				if(bfind(b[j].u)==bfind(b[j].v)&&fa[bfind(b[j].u)]==b[j].dat1&&fb[bfind(b[j].u)]==b[j].dat2)ans[b[j].name]=1;
				for(int t=cnt;t>=1;t--)
				{
					fa[qp[t].v]=qp[t].maxa;
					fb[qp[t].v]=qp[t].maxb;
					f[qp[t].u]=qp[t].data;
				}
			}
			
		}
		sort(a+1,a+1+r,cmp3);
	}
	for(int i=1;i<=q;i++)
	{
		if(ans[i])puts("Yes");
		else puts("No");
	}
	return 0; 
}
posted @ 2023-02-24 13:46  Gmt丶Fu9ture  阅读(41)  评论(0)    收藏  举报