bzoj4537: [Hnoi2016]最小公倍数

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4537

思路:把边按a排序,每sqrt(m)分一组

然后把询问按b排序,把在这组及以前的边按b排序

把这些边用并查集一条一条插入并维护

零散的部分暴力插入并记录,做完后暴力撤销

注意:并查集不能路径压缩,否则无法撤销回去

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010;
using namespace std;
int n,m,S,Q,top,f[maxn],maxa[maxn],maxb[maxn],ans[maxn],opcnt,siz[maxn];
struct data{
	int x,y,a,b,id;
	void init(int i){id=i,scanf("%d%d%d%d",&x,&y,&a,&b);}
	void print(){printf("%d %d %d %d %d\n",x,y,a,b,id);}
}e[maxn],q[maxn],stk[maxn];
struct oper{int x,y,f,maxa,maxb,siz;}op[maxn];
bool operator <(data x,data y){return x.a==y.a?x.b<y.b:x.a<y.a;}
bool cmp(data x,data y){return x.b==y.b?x.a<y.a:x.b<y.b;}
int find(int x){return f[x]==x?x:find(f[x]);}

void merge(int x,int y,int a,int b){
	x=find(x),y=find(y);
	if (siz[x]>siz[y]) swap(x,y);
	op[++opcnt]=(oper){x,y,f[x],maxa[y],maxb[y],siz[y]};
	if (x==y){maxa[y]=max(maxa[y],a),maxb[y]=max(maxb[y],b);return;}
	f[x]=y,siz[y]+=siz[x];
	maxa[y]=max(maxa[x],max(maxa[y],a));
	maxb[y]=max(maxb[x],max(maxb[y],b));
}

void goback(){
	for (int i=opcnt;i;i--){
		int x=op[i].x,y=op[i].y;
		f[x]=op[i].f,maxa[y]=op[i].maxa,maxb[y]=op[i].maxb,siz[y]=op[i].siz;
	}
	opcnt=0;
}

int main(){
	scanf("%d%d",&n,&m),S=(int)sqrt(m);
	for (int i=1;i<=m;i++) e[i].init(i);
	sort(e+1,e+1+m);
	//for (int i=1;i<=m;i++) e[i].print();
	scanf("%d",&Q);
	for (int i=1;i<=Q;i++) q[i].init(i);
	sort(q+1,q+1+Q,cmp);
	//for (int i=1;i<=Q;i++) q[i].print();
	for (int i=1;i<=m;i+=S){
		top=0;
		for (int j=1;j<=Q;j++)
			if (q[j].a>=e[i].a&&(i+S>m||q[j].a<e[i+S].a))
				stk[++top]=q[j];
		//printf("\ni: %d\n",i);for (int j=1;j<=top;j++) stk[j].print();
		sort(e+1,e+1+i,cmp);
		for (int j=1;j<=n;j++) f[j]=j,maxa[j]=maxb[j]=-1,siz[j]=1;
		for (int j=1,k=1;j<=top;j++){
			for (;k<i&&e[k].b<=stk[j].b;k++)
				merge(e[k].x,e[k].y,e[k].a,e[k].b);
			opcnt=0;
			for (int l=i;l<i+S&&l<=m;l++)
				if (e[l].a<=stk[j].a&&e[l].b<=stk[j].b)
					merge(e[l].x,e[l].y,e[l].a,e[l].b);
			int x=find(stk[j].x),y=find(stk[j].y);
			ans[stk[j].id]=(x==y&&maxa[x]==stk[j].a&&maxb[x]==stk[j].b);
			goback();
		}
	}
	for (int i=1;i<=Q;i++) puts(ans[i]?"Yes":"No");
	return 0;
}



转载于:https://www.cnblogs.com/thythy/p/5493638.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值