HNOI2016 最小公倍数 题解

HNOI2016 最小公倍数 题解

很神秘的分块优化暴力。

首先有一个暴力的想法,每次取出所有 \(a\le A,b\le B\) 的边,实际上就是判断 \(u,v\) 是否连通且连通块内是否有 \(a_{max}=A,b_{max}=B\)。这样是 \(O(mq)\) 的。

考虑对所有边按 \(a\) 排序,然后每 \(B\) 个分块。

我们考虑处理 \(A\) 在当前块 \(a\) 范围内的询问,那么前面的块的 \(a\) 一定满足限制。

考虑怎样满足 \(B\) 的限制,我们可以对前面块的所有边按 \(b\) 排序,然后双指针用并查集加边。

而对于当前块内同时满足 \(A,B\) 限制的边,这些边集并不是互相包含的,所以我们要独立贡献它们,用所有满足的边在缩完的并查集上跑搜索即可。

那么排序部分是 \(O(\frac {m^2}B\log m)\),对前面的块加边是 \(O(\frac {m^2}B\alpha(n))\),而对于块内的搜索是 \(O(qB\alpha(n))\) 的。

总复杂度应为 \(O(\frac {m^2}B\log m+qB)\)。取 \(B=O(\frac m{\sqrt q})\) 时,复杂度达到 \(O(m\sqrt q\log m)\)

细节

  • 对于连向同一连通块内的边,记得更新 \(maxa,maxb\)

  • 记得询问和边都要排序。

  • 由于有询问的 \(a,b=0\) 的情况,所以 \(maxa,maxb\) 的初值需要设为 \(-1\)

  • 如果是用指针扫询问,记得要把询问的 \(a\) 小于边权的 \(\min\{a\}\) 的询问过滤掉。

AC 代码:

const int N=1e5+5;
const int B=448;
int n,m;
struct arr{
	int u,v,a,b,num;
	void Read(){
		return read(u,v,a,b);
	}
};
int fa[N],sz[N];
int getfa(int x){
	if(fa[x]==x)return x;
	return fa[x]=getfa(fa[x]);
}
int maxa[N],maxb[N];
void merge(int x,int y,int a,int b){
	x=getfa(x),y=getfa(y);
	if(x==y){
		maxa[x]=max(maxa[x],a);
		maxb[x]=max(maxb[x],b);
		return ;
	}
	if(sz[x]<sz[y])swap(x,y);
	maxa[x]=max(maxa[x],maxa[y]);
	maxb[x]=max(maxb[x],maxb[y]);
	maxa[x]=max(maxa[x],a);
	maxb[x]=max(maxb[x],b);
	sz[x]+=sz[y];
	fa[y]=x;
}
arr e[N],q[N];
int Q;
int ans[N];
int vis[N],bid;
struct edge{
	int v,a,b;
};
vector<edge> g[N];
signed main(){
	read(n,m);
	fo(i,1,m){
		e[i].Read();
	}
	sort(e+1,e+m+1,[](arr a,arr b){
		return a.a<b.a;
	});
	read(Q);
	fo(i,1,Q){
		q[i].Read();
		q[i].num=i;
	}
	sort(q+1,q+1+Q,[](arr a,arr b){
		return a.a<b.a;
	});
	int L=1;
	e[m+1].a=1e9+1;
	while(L<=Q&&q[L].a<e[1].a)++L;
	for(int l=1,r;l<=m;l=r+1){
		r=min(m,l+B-1);
		sort(e+1,e+l,[](arr a,arr b){
			return a.b<b.b;
		});
		int R=L-1;
		while(R<Q&&q[R+1].a>=e[l].a&&q[R+1].a<=e[r].a&&q[R+1].a<e[r+1].a)++R;
		fo(i,1,n)fa[i]=i,sz[i]=1,maxa[i]=maxb[i]=-1;
		int t=1;
		sort(q+L,q+R+1,[](arr a,arr b){
			return a.b<b.b;
		});
		fo(i,L,R){
			while(t<l&&e[t].b<=q[i].b){
				merge(e[t].u,e[t].v,e[t].a,e[t].b);
				++t;
			}
			vector<int> vec;
			fo(j,l,r){
				if(e[j].a<=q[i].a&&e[j].b<=q[i].b){
					int a=getfa(e[j].u),b=getfa(e[j].v);
					g[a].push_back({b,e[j].a,e[j].b}),g[b].push_back({a,e[j].a,e[j].b}),vec.push_back(a),vec.push_back(b);
				}
			}
			++bid;
			queue<int> p;
			p.push(getfa(q[i].u)),vis[p.front()]=bid;
			int ma=-1,mb=-1,bz=0;
			while(p.size()){
				int u=p.front();
				p.pop();
				if(u==getfa(q[i].v))bz=1;
				ma=max(ma,maxa[u]),mb=max(mb,maxb[u]);
				for(auto v:g[u]){
					if(vis[v.v]!=bid){
						vis[v.v]=bid;
						p.push(v.v);
					}
					ma=max(ma,v.a),mb=max(mb,v.b);
				}
			}
			if(bz&&ma==q[i].a&&mb==q[i].b)ans[q[i].num]=1;
			for(auto x:vec)g[x].clear();
		}
		L=R+1;
	}
	fo(i,1,Q)if(ans[i])write("Yes\n");
	else write("No\n");
	return 0;
}
posted @ 2024-12-25 22:18  dengchengyu  阅读(24)  评论(0)    收藏  举报