[分块 并查集] BZOJ 4537 [Hnoi2016]最小公倍数

题解:http://www.cnblogs.com/y7070/p/5412793.html


暴力分块


”对于每一组,将符合一组a的询问选出来,将这些询问和这一块之前的边(a一定小于这些询问)按b排序,然后交替插入,询问,对于一个询问,在当前块也有可能有满足的边,我们将其加入,考虑后并撤销“


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x){
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;

struct event{
	int u,v,a,b;
	int idx;
}A[N],Q[N],tmp[N];
int cnt;


bool cmpa(const event A,const event B){
	return A.a<B.a;
}
bool cmpb(const event A,const event B){
	return A.b<B.b;
}

int n,m,tot;
int ans[N];

struct Op{
	int x,y,xmaxa,xmaxb,ymaxa,ymaxb,ysize;
	Op(int x=0,int y=0,int xmaxa=0,int xmaxb=0,int ymaxa=0,int ymaxb=0,int ysize=0):x(x),y(y),xmaxa(xmaxa),xmaxb(xmaxb),ymaxa(ymaxa),ymaxb(ymaxb),ysize(ysize) {}
}op[N];
int pnt;

int fat[N],maxa[N],maxb[N],size[N];

inline int Fat(int u){
	return u==fat[u]?u:Fat(fat[u]);
}

inline void Merge(int u,int v,int a,int b){
	u=Fat(u); v=Fat(v);
	if (size[u]>size[v]) swap(u,v);
	op[++pnt]=(Op){u,v,maxa[u],maxb[u],maxa[v],maxb[v],size[v]};
	if (u==v)
	{
		maxa[u]=max(maxa[u],a);
		maxb[u]=max(maxb[u],b);
		return;
	}
	size[v]+=size[u];
	maxa[v]=max(maxa[v],max(a,maxa[u]));
	maxb[v]=max(maxb[v],max(b,maxb[u]));
	fat[u]=v;
}

inline void Restore(){
	for (int i=pnt;i;i--)
	{
		maxa[op[i].x]=op[i].xmaxa;
		maxb[op[i].x]=op[i].xmaxb;
		maxa[op[i].y]=op[i].ymaxa;
		maxb[op[i].y]=op[i].ymaxb;
		size[op[i].y]=op[i].ysize;
		fat[op[i].x]=op[i].x;
	}
}

int main()
{
	int st,ed,SIZE;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n); read(m); SIZE=sqrt(n);
	for (int i=1;i<=m;i++) read(A[i].u),read(A[i].v),read(A[i].a),read(A[i].b);
	read(tot);
	for (int i=1;i<=tot;i++) read(Q[i].u),read(Q[i].v),read(Q[i].a),read(Q[i].b),Q[i].idx=i;
	sort(A+1,A+m+1,cmpa);
	sort(Q+1,Q+tot+1,cmpb);
	for (st=1;st<=m;st=ed+1)
	{
		ed=min(st+SIZE-1,m);
		cnt=0;
		for (int i=1;i<=tot;i++)
			if (Q[i].a>=A[st].a && (ed==m || Q[i].a<A[ed+1].a))	
				tmp[++cnt]=Q[i];
		if (!cnt) continue;
		sort(A+1,A+st,cmpb);
		for (int i=1;i<=n;i++) fat[i]=i,size[i]=1,maxa[i]=maxb[i]=-1;
		for (int i=1,j=1;i<=cnt;i++)
		{
			while (j<st && A[j].b<=tmp[i].b)
				Merge(A[j].u,A[j].v,A[j].a,A[j].b),j++;
			pnt=0;
			for (int k=st;k<=ed;k++)
				if (A[k].a<=tmp[i].a && A[k].b<=tmp[i].b)
					Merge(A[k].u,A[k].v,A[k].a,A[k].b);
			int fx=Fat(tmp[i].u),fy=Fat(tmp[i].v);
			if (fx==fy && maxa[fx]==tmp[i].a && maxb[fx]==tmp[i].b) 
				ans[tmp[i].idx]=1;
			Restore();
		}
	}
	for (int i=1;i<=tot;i++)
		ans[i]?printf("Yes\n"):printf("No\n");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值