bzoj4537[HNOI2016]最小公倍数

如果信息只有一维,就可以直接排序,扫一遍的过程中用并查集来维护,也就是u和v需要连通且连通块内的最大边权等于询问的边权。现在有两维,我一开始还往分治的方向去想,但是询问所需要的在每一层中的信息不好合并,于是就考虑分块。
首先把边按a排序,分块,把询问挂在第一个a大于询问a的那条边所在的块上,然后每一块内的边,每一块上的询问按b排序。
从左往右扫每个块,并把这个块左边的所有边都加入并查集中,且这个块之前的边按b归并排序好,对于一个块中的询问,记一个指针表示这个块之前的那些块中最多可以加到哪条边(按b比较),然后暴力扫当前块中符合它要求的边(\(a\le A_q且 b\le B_q\))加入并查集中,得到答案后就把当前块的这几条边撤回。每做完一个块就把它和之前的块按b归并排序。
可能讲的不大清楚,懂了大致意思后可以自己继续想一想细节问题。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<ctime>
#define P puts("lala")
#define cp cerr<<"lala"<<endl
#define fi first
#define se second
#define ln putchar('\n')
#define pb push_back
#define shmem(x) cerr<<sizeof(x)/(1024*1024.0)<<"MB"<<endl
using namespace std;
inline int read()
{
    char ch=getchar();int g=1,re=0;
    while(ch<'0'||ch>'9'){if(ch=='-')g=-1; ch=getchar();}
    while(ch<='9'&&ch>='0') re=(re<<1)+(re<<3)+(ch^48),ch=getchar();
    return re*g;
}
typedef long long ll;
typedef pair<int,int> pii;

const int N=100050;
struct node
{
	int x,y,a,b,id;
	node(int x=0,int y=0,int a=0,int b=0,int id=0):x(x),y(y),a(a),b(b),id(id) { }
};
inline bool operator < (node a,node b) {return a.b<b.b;}
inline bool cmp1(node a,node b) {return a.a<b.a;}
inline bool cmp2(node a,node b) {return a.b<b.b;}
node e[N],q[N];
int n,m,blosiz,bel[N],Ans[N];
vector<node>ve[500];

int fa[N];
inline int find(int x)
{
	while(fa[x]!=x) x=fa[x];
	return x;
}
node stk[N]; int top=0;
int maxa[N],maxb[N],siz[N];
inline void merge(int x,int y,int a,int b)
{
	x=find(x); y=find(y);
	if(siz[x]>siz[y]) swap(x,y);
	stk[++top]=node(x,y,maxa[y],maxb[y],siz[y]);
	maxa[y]=max(maxa[y],max(maxa[x],a)); maxb[y]=max(maxb[y],max(maxb[x],b));
	if(x==y) return ;
	siz[y]+=siz[x]; fa[x]=y;
}
inline void undo()
{
	int x=stk[top].x,y=stk[top].y;
	siz[y]=stk[top].id; maxa[y]=stk[top].a; maxb[y]=stk[top].b;
	fa[x]=x;
	top--;
}

node now[N],tmp[N]; int tot=0;
void mergesort(int l,int r)
{
	int p1=1,p2=l,p3=1;
	while(p1<=tot&&p2<=r)
	{
		if(now[p1]<e[p2]) tmp[p3]=now[p1],p1++,p3++;
		else tmp[p3]=e[p2],p2++,p3++;
	}
	while(p1<=tot) tmp[p3]=now[p1],p1++,p3++;
	while(p2<=r) tmp[p3]=e[p2],p2++,p3++;
	tot+=r-l+1;
	for(int i=1;i<=tot;++i) now[i]=tmp[i];
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in","r",stdin);freopen("1.out","w",stdout);
#endif
	n=read(); m=read();
	for(int i=1;i<=m;++i) 
		e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
	int Q=read();
	for(int i=1;i<=Q;++i) 
		q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
	
	sort(e+1,e+1+m,cmp1);
	blosiz=sqrt(m);
	for(int i=1;i<=m;++i) bel[i]=(i-1)/blosiz+1;

	sort(q+1,q+1+Q,cmp1);
	for(int i=1,p1=1;i<=m;++i)
	{
		while(p1<=Q&&(q[p1].a<e[i].a||i==m)) ve[bel[i]].pb(q[p1]),p1++;
	}
	for(int i=1;i<=bel[m];++i) sort(ve[i].begin(),ve[i].end());
	for(int i=1;i<=bel[m];++i) sort(e+(i-1)*blosiz+1,e+min(i*blosiz,m)+1);

	for(int o=1;o<=bel[m];++o)
	{
		top=0;
		for(int i=1;i<=n;++i) fa[i]=i,maxa[i]=maxb[i]=-1,siz[i]=1;
		int siz=ve[o].size(),p1=1;
		for(int w=0;w<siz;++w)
		{
			while(p1<=tot&&ve[o][w].b>=now[p1].b) //in front of the block
				merge(now[p1].x,now[p1].y,now[p1].a,now[p1].b),p1++;
			int last=top;
			for(int i=(o-1)*blosiz+1;i<=m&&i<=o*blosiz;++i) //in the block
				if(e[i].b<=ve[o][w].b)
				{
					if(e[i].a<=ve[o][w].a)
						merge(e[i].x,e[i].y,e[i].a,e[i].b);
				}
				else break;
			int r1=find(ve[o][w].x),r2=find(ve[o][w].y);
			if(r1==r2&&maxa[r1]==ve[o][w].a&&maxb[r1]==ve[o][w].b)
				Ans[ve[o][w].id]=1;
			while(top!=last) undo();
		}
		mergesort((o-1)*blosiz+1,min(o*blosiz,m));
	}
	for(int i=1;i<=Q;++i) puts(Ans[i]==1?"Yes":"No");
	return 0;
}
posted @ 2018-03-17 22:09  BLMontgomery  阅读(145)  评论(0编辑  收藏  举报