【bzoj4537】 Hnoi2016—最小公倍数

http://www.lydsy.com/JudgeOnline/problem.php?id=4537 (题目链接)

题意

  给出一个${n}$个点${m}$条边的无向图,每条边有两个权值${a,b}$。给出$Q$个询问,问$u,v$两点间是否存在一条路径(可以不是简单路径),使得路径上的边的最大$a,b$正好等于$A,B$。

Solution

  http://blog.csdn.net/zmh964685331/article/details/51194393

  我们考虑按照$a$的权值分块。每次找到$a$的权值在块内的询问,对它们进行处理。

  将这些询问按照$b$的大小排序以后,我们用并查集维护图的连通性。此时的边有两类,第一类就是在之前的块中的边,这些边的$a$肯定是小于询问的$a$的,我们将这些边按照$b$排序,然后依次加入。第二类就是在当前块中的边,这些边我们不好处理,直接暴for过去,如果它们的$a,b$都小于询问的$a,b$,就加入到图中,做完之后再将这些边暴力还原,因为只有$sqrt{m}$条,所以不虚。

  并查集按秩合并,同时维护连通块中$a,b$的最值。查询的时候就是判断是否$u,v$在同一连通块中,这个连通块$a,b$的最大值是否正好等于询问的$a,b$。

细节

  块的大小,开大一点,不然会TLE。

  如果大块大块相同的$a$,我们的询问会被重复不断的加入,这是很浪费的,所以我们对这种情况进行一些处理。

  bzoj是有多慢,本机20s,交上去几乎要TLE。。。

代码

// bzoj4537
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf (1ll<<30)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
inline int gi() {
	int x=0,f=1;char ch=getchar();
	while (ch<'0' || ch>'9') {if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int maxn=100010;
int ans[maxn],Q,n,m,top,tot,S;
int fa[maxn],da[maxn],db[maxn],size[maxn];
struct option {int u,v,da,db,fa,size;}op[maxn];
struct data {int u,v,a,b,id;}e[maxn],q[maxn],tmp[maxn];

inline bool cmpa(data a,data b) {return a.a==b.a ? a.b<b.b : a.a<b.a;}
inline bool cmpb(data a,data b) {return a.b==b.b ? a.a<b.a : a.b<b.b;}

inline int find(int x) {
	return fa[x]==x ? x : find(fa[x]);
}
inline void Union(int u,int v,int a,int b) {
	u=find(u),v=find(v);
	if (size[u]>size[v]) swap(u,v);
	op[++tot]=(option){u,v,da[v],db[v],fa[u],size[v]};
	if (u==v) da[v]=max(da[v],a),db[v]=max(db[v],b);
	else {
		fa[u]=v;size[v]+=size[u];
		da[v]=max(da[v],max(da[u],a));
		db[v]=max(db[v],max(db[u],b));
	}
}
int main() {
	n=gi(),m=gi();
	for (int i=1;i<=m;i++) e[i].u=gi(),e[i].v=gi(),e[i].a=gi(),e[i].b=gi(),e[i].id=i;
	S=(int)sqrt(3*m);
	Q=gi();
	for (int i=1;i<=Q;i++) q[i].u=gi(),q[i].v=gi(),q[i].a=gi(),q[i].b=gi(),q[i].id=i;
	sort(e+1,e+1+m,cmpa);
	sort(q+1,q+1+Q,cmpb);
	for (int i=1;i<=m;i+=S) {
		int L=i,R=min(i+S-1,m);
		top=0;
		for (int j=1;j<=Q;j++)
			if (q[j].a>=e[L].a && (R==m || q[j].a<e[R+1].a)) tmp[++top]=q[j];   //考虑很多边a相等的情况
		sort(e+1,e+L,cmpb);
		for (int j=1;j<=n;j++) fa[j]=j,size[j]=1,da[j]=db[j]=-1;
		for (int j=1,k=1;j<=top;j++) {
			for (;k<L && e[k].b<=tmp[j].b;k++) Union(e[k].u,e[k].v,e[k].a,e[k].b);
			tot=0;
			for (int l=L;l<=R;l++)
				if (e[l].a<=tmp[j].a && e[l].b<=tmp[j].b) Union(e[l].u,e[l].v,e[l].a,e[l].b);
			int u=find(tmp[j].u),v=find(tmp[j].v);
			ans[tmp[j].id]= u==v && da[u]==tmp[j].a && db[u]==tmp[j].b;
			for (;tot;tot--) {
				fa[op[tot].u]=op[tot].fa;
				db[op[tot].v]=op[tot].db;
				da[op[tot].v]=op[tot].da;
				size[op[tot].v]=op[tot].size;
			}
		}	 
	}
	for (int i=1;i<=Q;i++) printf(ans[i] ? "Yes
" : "No
");
	return 0;
}
原文地址:https://www.cnblogs.com/MashiroSky/p/6405710.html