P3247 [HNOI2016]最小公倍数

题目链接

题意分析

这道题的题意:给定一个无向图,每一次询问u与v之间是否存在一条路径使得ai最大值是a,bi最大值是b

我一开始以为是离线+瓶颈树

后来发现 这道题必须是恰好等于 而不是大于等于或者小于等于

我们看一下 这道题如果路径上只有一个权值的话 我们可以使用双指针

把对于当前询问ai 路径中的ai小于等于这个ai的我们用带权并查集维护 然后最大值是否等于就可以了

但是很不幸的是有两个

无耻的参考了dalao的见解之后

对原始路径按照ai进行排序 对询问按照bi排序

对原始的边来一个分块

我们先枚举块 并暴力枚举询问 把ai在当前块的先存起来

然后我们对于之前的块按照bi排序

那么加入的询问以及排好序的一部分我们只需要考虑bi就可以了

因为现在我们使用的边都是ai小于等于询问的ai 所以只需要考虑ai最大值等于询问的ai就可以了

由于只考虑bi并且都是有序的 我们使用双指针就可以了

双指针思路同上

对于当前块内的我们暴力添加就可以了

但是要注意 由于询问的ai还是无序的

所以添加的块内的边我们需要从并查集内删除

也就是可撤销并查集 利用栈就可以了

具体参见代码

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#define N 100008
using namespace std;
int n,m,q,S,top,tot,pos;
int bel[N],cdy[N],wzy[N],deep[N],ans[N];
struct Edge
{
	int u,v,a,b,id;
}ed[N],qe[N],tmp[N];
struct Node
{
	int x,y,deep,a,b;
}sta[N];

bool cmp1(const Edge &A,const Edge &B)
{return A.a==B.a ? A.b<B.b : A.a<B.a;}
bool cmp2(const Edge &A,const Edge &B)
{return A.b==B.b ? A.a<B.a : A.b<B.b;}

int find(int x)
{return bel[x]==x ? x : find(bel[x]);}

void merge(Edge X)
{
	int x=find(X.u),y=find(X.v);
	if(deep[x]<deep[y]) swap(x,y);
	sta[++top]=(Node){x,y,deep[x],cdy[x],wzy[x]};//入栈 
	cdy[x]=max(cdy[x],X.a);wzy[x]=max(wzy[x],X.b);
	if(x==y) return;
	bel[y]=x;deep[x]=max(deep[x],deep[y]+1);
	cdy[x]=max(cdy[x],cdy[y]);
	wzy[x]=max(wzy[x],wzy[y]);
}
bool query(int id)
{
	int x=find(tmp[id].u),y=find(tmp[id].v);
	if(x!=y) return 0;
	if(cdy[x]!=tmp[id].a || wzy[x]!=tmp[id].b) return 0;
	return 1;
}
void del(int now)
{
	int x=sta[now].x,y=sta[now].y;
	bel[y]=y;deep[x]=sta[now].deep;
	cdy[x]=sta[now].a;wzy[x]=sta[now].b;
}
int main()
{
	cin>>n>>m;S=sqrt(m);
	for(int i=1;i<=m;++i)
	cin>>ed[i].u>>ed[i].v>>ed[i].a>>ed[i].b; 
	
	cin>>q;
	for(int i=1;i<=q;++i)
	cin>>qe[i].u>>qe[i].v>>qe[i].a>>qe[i].b,qe[i].id=i;
	
	sort(ed+1,ed+m+1,cmp1);sort(qe+1,qe+q+1,cmp2);
	for(int i=1;i<=m;i+=S)
	{
		tot=0;pos=1;
		for(int j=1;j<=n;++j) bel[j]=j,cdy[j]=wzy[j]=-1,deep[j]=0;
		for(int j=1;j<=q;++j)
		if(qe[j].a>=ed[i].a && (i+S>m || qe[j].a<ed[i+S].a))
		tmp[++tot]=qe[j];
		
		if(tot==0) continue;
		if(i!=1) sort(ed+1,ed+i,cmp2);//前面的块我们就可以双指针了 
		
		for(int j=1;j<=tot;++j)
		{
			while(pos<i && ed[pos].b<=tmp[j].b)
			merge(ed[pos]),++pos;//双指针维护 
			top=0;//top : 这里是当前块中的 不是普遍适用的 所以我们要入栈然后删掉 
			for(int k=i;k<i+S&&k<=m;++k)
			if(ed[k].a<=tmp[j].a&&ed[k].b<=tmp[j].b)
			merge(ed[k]);
			ans[tmp[j].id]=query(j);
			while(top) del(top--); //删栈 
		} 
	}
	for(int i=1;i<=q;++i)
	if(ans[i]) puts("Yes");
	else puts("No");
	return 0;
} 
posted @ 2020-08-23 21:17  tcswuzb  阅读(148)  评论(0编辑  收藏  举报