把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

P3247 [HNOI2016] 最小公倍数 分析

题目概述

给一个无向图,每条边上面有边权 \(a,b\),现在有多个询问,问是否存在一条可重复经过的路径(\(u\rightarrow\))使得最大的 \(a\) 小于某个值,最大的 \(b\) 小于某个值。

分析

首先显然得到把 \(\leq a,\leq b\) 的边取出来然后并查集判断即可。

套路

两个值的偏序询问有个套路就是你分块,然后全部是按照 \(a\) 排序,块内是按照 \(b\) 排序,询问再按照 \(a\) 排序。

这有什么好处呢?

这样某个块可以有个 \(a\) 的范围,然后处理询问中在这个范围内的。

具体分析

怎么处理呢?可以将前面的块按 \(b\) 排序(因为 \(a\) 已经满足条件了),同时也可以对这批询问按照 \(b\) 来排序,然后批量处理这个询问的时候可以用尺取法在并查集中加入肯定不会撤销的边。

然后对于块内,由于每个询问的 \(a,b\) 在块内是没有什么规律的,这个就要撤销,这里直接暴力即可。

代码

时间复杂度 \(\mathcal{O}(n\sqrt n+m\sqrt n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <cmath>
#include <stack>
#include <numeric>
#define int long long
#define N 100005
#define isdigit(ch) ('0' <= ch && ch <= '9')
using namespace std;
template<typename T>
void read(T &x) {
	x = 0;
	char ch = getchar();
	for (;!isdigit(ch);ch = getchar());
	for (;isdigit(ch);ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
}
template<typename T>
void write(T x) {
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
struct edge{
	int u,v,a,b;
}edges[N];
struct query{
	int u,v,a,b,id;
}q[N];
bool ans[N];
struct DSU{
	int fa[N],sz[N],mxa[N],mxb[N],n;
	struct change{
		int type;
		int child,fa;
		int olda,oldb,oldsz;
	};
	stack<change> sta;
	void init(int _n) {
		n = _n; 
		for (int i = 1;i <= n;i ++) fa[i] = i,sz[i] = 1,mxa[i] = mxb[i] = -1;
	}
	int find(int x) {return x == fa[x] ? x : find(fa[x]);}
	void add1(const edge&x) {
		int xx = find(x.u),yy = find(x.v);
		if (xx == yy) mxa[xx] = max(mxa[xx],x.a),mxb[xx] = max(mxb[xx],x.b);
		else {
			if (sz[xx] < sz[yy]) swap(xx,yy);
			fa[yy] = xx;
			sz[xx] += sz[yy];
			mxa[xx] = max({mxa[xx],mxa[yy],x.a}),mxb[xx] = max({mxb[xx],mxb[yy],x.b});
		}
	}
	void add2(const edge&x) {
		int xx = find(x.u),yy = find(x.v);
		if (xx == yy) {
			sta.push({0,xx,xx,mxa[xx],mxb[xx],sz[xx]});
			mxa[xx] = max(mxa[xx],x.a),mxb[xx] = max(mxb[xx],x.b);
		}
		else {
			if (sz[xx] < sz[yy]) swap(xx,yy);
			sta.push({1,yy,xx,mxa[xx],mxb[xx],sz[xx]});
			fa[yy] = xx;
			sz[xx] += sz[yy];
			mxa[xx] = max({mxa[xx],mxa[yy],x.a}),mxb[xx] = max({mxb[xx],mxb[yy],x.b});
		}
	}
	void back(int tot) {
		while(sta.size() > tot) {
			change t = sta.top();
			sta.pop();
			int xx = t.fa,yy = t.child;
			if (t.type == 0) mxa[xx] = t.olda,mxb[xx] = t.oldb;
			else {
				mxa[xx] = t.olda,mxb[xx] = t.oldb;
				sz[xx] = t.oldsz;
				fa[yy] = yy;
			}
		}
	}
}dsu;
int n,m,len,Q,bl[N];
signed main(){
	read(n),read(m);
	len = ceil(sqrt(m));
	for (int i = 1;i <= m;i ++) scanf("%lld%lld%lld%lld",&edges[i].u,&edges[i].v,&edges[i].a,&edges[i].b);
	sort(edges + 1,edges + 1 + m,[](const edge& x,const edge& y) {
		return x.a < y.a;
	});
	vector<int> L,R,A;
	L.reserve(m / len + 2),R.reserve(m / len + 2),A.reserve(m / len + 2);
	L.push_back(0),R.push_back(0),A.push_back(0);
	for (int i = 1; i <= m; i++) {
	    bl[i] = (i - 1) / len + 1;
	    if (i == 1 || (i - 1) / len != i / len) {
	        L.push_back(i);
	        R.push_back(i);
	    }
	    R.back() = i;
	}
	for (int i = bl[1];i <= bl[m];i ++) A.push_back(edges[L[i]].a);
	A.push_back(1e18);
	read(Q);
	for (int i = 1;i <= Q;i ++) scanf("%lld%lld%lld%lld",&q[i].u,&q[i].v,&q[i].a,&q[i].b),q[i].id = i;
	vector<int> qid(Q);
	iota(qid.begin(),qid.end(),1);
	sort(qid.begin(),qid.end(),[&](int x,int y) {
		return q[x].a < q[y].a;
	});
	for (int i = bl[1];i <= bl[m];i ++) {
		int xa = A[i],ya = A[i + 1];
		auto it1 = lower_bound(qid.begin(),qid.end(),xa,[&](const int &id,const int &val) {
			return q[id].a < val;
		});
		auto it2 = lower_bound(qid.begin(),qid.end(),ya,[&](const int &id,const int &val) {
			return q[id].a < val;
		});
		vector<int> cur(it1,it2);
		if (cur.empty()) continue;
		sort(cur.begin(),cur.end(),[&](int x,int y) {
			return q[x].b < q[y].b;
		});
		vector<edge> pre;
		pre.reserve(L[i] - 1);
		for (int j = 1;j < L[i];j ++) pre.push_back(edges[j]);
		dsu.init(n);
		sort(pre.begin(),pre.end(),[](const edge&x,const edge&y) {
			return x.b < y.b;
		});
		int p = 0;
		for (int id : cur) {
			while(p < (int)pre.size() && pre[p].b <= q[id].b) dsu.add1(pre[p]),p ++;
			int ori = dsu.sta.size();
			int pos = upper_bound(edges + L[i], edges + R[i] + 1, q[id].a,
                [](int val, const edge &e) {
                    return val < e.a;
                }) - edges;
			for (int j = L[i];j < pos;j ++)
				if (edges[j].b <= q[id].b) dsu.add2(edges[j]);
			int xx = dsu.find(q[id].u),yy = dsu.find(q[id].v);
			if (xx == yy && q[id].a == dsu.mxa[xx] && q[id].b == dsu.mxb[xx]) ans[id] = 1;
			dsu.back(ori);
		}
	}
	for (int i = 1;i <= Q;i ++) cout << (ans[i] ? "Yes" : "No") << '\n';
	return 0;
} 
posted @ 2026-07-21 21:32  high_skyy  阅读(12)  评论(0)    收藏  举报
动态线条
动态线条end
浏览器标题切换
浏览器标题切换end
💬 加载中……