[HNOI 2016] 最小公倍数

题目

传送门

前言

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

解法

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

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

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

解了一下 (B) 的最优取值:(sqrt{frac{m^2}{qcdot 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;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15144054.html