题目

传送门

前言

来来回回调了好久… 各种 \(\mathtt{qqgg}\) 的锅层出不穷。最后 \(\mathtt{T}\) 飞了以为是自己常数大,结果是之前调的时候为了方便改了一个循环初始变量 —— 单点询问 \(\mathcal O(m\cdot \log n)\) 复杂度 \(\text{get}\)

解法

首先有一个暴力:对于每个询问 \(i\),枚举所有 \(a\le a_i,b\le b_i\) 的边加入带权并查集,然后查询 \((u_i,v_i)\) 是否连通且连通块的 \(\max a=a_i,\max b=b_i\) —— 单点询问 \(\mathcal O(m\cdot \alpha (n))\) 复杂度 \(\text{get}\)!(悲

将边从小到大以 \(a\) 排序并分块,将询问从小到大以 \(b\) 排序。对于每个询问 \(i\),找到一个块 \(j\),使得在 \([1,j]\) 中的块中的边都满足 \(a\le a_i\),并把 \(i\) 加入集合 \(be_j\) 中。枚举 \(i=[0,cnt]\)(其中 \(cnt\) 是块的总数),将属于前 \(i\) 个块的边从小到大以 \(b\) 排序。再枚举 \(j\in be_i\)(按 \(b\) 从小到大的顺序),这样属于前 \(i\) 个块的边可以用 \(\text{two-pointers}\) 的方式加入,复杂度 \(\mathcal O\left ((B+m)\cdot \frac{m}{B}\cdot \alpha (n)\right)=\mathcal O\left (\left(m+ \frac{m^2}{B}\right)\cdot \alpha (n)\right)\)(等差数列求和)。对于第 \(i+1\) 块的边就只有暴力判断,但此时的并查集需要带撤销,可能是 \(\mathcal O(qB\cdot \log n)\) 的?

至于 "将属于前 \(i\) 个块的边从小到大以 \(b\) 排序" 操作可以使用归并排序来减少复杂度。

解了一下 \(B\) 的最优取值:\(\sqrt{\frac{m^2}{q\cdot \log n}}\)不知道有啥用。

其实也可以不使用带撤销并查集。加入前 \(i\) 个块的边后将其缩点,然后对于第 \(i+1\) 个块的边进行 \(\mathtt{bfs}\) 即可。

总结

当需要维护偏序时,可以先分块解决一个偏序。再在某块中暴力计算答案。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
} 

#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn=5e4+5,maxm=1e5+5;
const int maxb=320;

vector <int> be[maxb];
int n,m,B,R[maxb],q,ans[maxn];
struct Edge {
	int u,v,a,b;
	
	bool operator < (const Edge &y) const {
		return a<y.a;
	}
} e[maxm];
struct Query {
	int u,v,a,b,id;
	
	bool operator < (const Query &y) const {
		return b<y.b;
	}
} s[maxn];
struct DSU {
	int tp,f[maxn],rk[maxn];
	int ma[maxn],mb[maxn];
	struct store {
		int U,V,A,B,Rk;
	} sta[maxm];
	
	void init() {
		for(int i=1;i<=n;++i)
			f[i]=i,rk[i]=1,
			ma[i]=mb[i]=-1;
	}
	
	int Find(int x) {
		return x==f[x]?x:Find(f[x]);
	}
	
	void Merge(int u,int v,int aa,int bb) {
		u=Find(u),v=Find(v);
		if(rk[u]<rk[v]) swap(u,v);
		sta[++tp]=(store){u,v,ma[u],mb[u],rk[u]};
		if(u^v) {
			f[v]=u,rk[u]+=rk[v];
		}
		ma[u]=max(ma[u],max(ma[v],aa));
		mb[u]=max(mb[u],max(mb[v],bb));
	}
	
	int FindSet(int x) {
		return x==f[x]?x:f[x]=FindSet(f[x]);
	}
	
	void Unite(int u,int v,int aa,int bb) {
		u=FindSet(u),v=FindSet(v);
		if(rk[u]<rk[v]) swap(u,v);
		if(u^v) {
			f[v]=u,rk[u]+=rk[v];
		}
		ma[u]=max(ma[u],max(ma[v],aa));
		mb[u]=max(mb[u],max(mb[v],bb));
	}
	
	void rec() {
		store t;
		while(tp) {
			t=sta[tp--];
			f[t.V]=t.V,rk[t.U]=t.Rk;
			ma[t.U]=t.A,mb[t.U]=t.B;
		}
	}
} dsu;

bool cmp(const Edge &x,const Edge &y) {
	return x.b<y.b;
}

void Sort(int bl) {
	static Edge t[maxm];
	static int i,j,cnt,r;
	i=cnt=0; r=j=(bl-1)*B;
	while(i<r and j<R[bl]) {
		if(e[i+1].b<e[j+1].b)	
			t[++cnt]=e[++i];
		else t[++cnt]=e[++j];
	}
	while(i<r) t[++cnt]=e[++i];
	while(j<R[bl]) t[++cnt]=e[++j];
	for(int i=1;i<=R[bl];++i)
		e[i]=t[i];
}

int main() {
	n=read(9),m=read(9);
	B=sqrt(1.0*m);
	int lim=(m-1)/B+1;
	for(int i=1;i<=m;++i)
		e[i].u=read(9),e[i].v=read(9),
		e[i].a=read(9),e[i].b=read(9);
	sort(e+1,e+m+1);
	for(int i=1;i<=lim;++i)
		R[i]=min(m,i*B);
	q=read(9);
	for(int i=1;i<=q;++i) {
		s[i].u=read(9),s[i].v=read(9),
		s[i].a=read(9),s[i].b=read(9);
		s[i].id=i;
	}
	sort(s+1,s+q+1);
	for(int i=1;i<=q;++i) {
		int j=0;
		while(j<lim and e[R[j+1]].a<=s[i].a)
			++j;
		be[j].push_back(i);
	}
	for(int i=1;i<=lim;++i)
		sort(e+(i-1)*B+1,e+R[i]+1,cmp);
	
	for(int i=0;i<=lim;++i) {
		dsu.init(); 
		
		int pos=0;
		for(int J=0;J<be[i].size();++J) {
			int j=be[i][J];
			while(pos<R[i] and e[pos+1].b<=s[j].b)
				++pos,dsu.Unite(e[pos].u,e[pos].v,e[pos].a,e[pos].b);
			for(int k=i*B+1;k<=R[i+1];++k) {
				if(s[j].b<e[k].b) break;
				if(s[j].a>=e[k].a)
					dsu.Merge(e[k].u,e[k].v,e[k].a,e[k].b);
			}
			s[j].u=dsu.Find(s[j].u),s[j].v=dsu.Find(s[j].v);
			ans[s[j].id]=(s[j].u==s[j].v and dsu.ma[s[j].u]==s[j].a and dsu.mb[s[j].u]==s[j].b);
			dsu.rec();
		}
		if(i^lim) Sort(i+1);
	}
	
	for(int i=1;i<=q;++i)
		puts(ans[i]?"Yes":"No");
	return 0;
}
posted on 2021-08-15 17:34  Oxide  阅读(37)  评论(0编辑  收藏  举报