CF763E Timofey and our friends animals

\(\tt Analysis\)

多次、区间、询问、问题不常规……考虑 莫队

在莫队指针移动的途中,\(k\) 很小,因此边可以暴力加一个点的边。

至于联通块个数……这个并查集维护就行了。

并查集难以支持撤销(不然很难做到好的复杂度)。于是我们把莫队换成回滚莫队,然后就只需要普通并查集就行了。

\(\tt Details\)

莫队的回滚部分最后需要撤销所有并查集 fa 的改变。

因此这部分的并查集不能路径压缩,而前面的增部分可以路径压缩。

所以如果不写并查集按秩合并可能会挂(但是不知道为什么写了按秩合并比不写常数大一点)。

// C++20

const int N = 1e5 + 5,M = 5e5 + 5;

int n,k,m,q,B,bl[N],ans[N];
std::vector<int> E[N],P;
std::vector<std::tuple<int,int,int>> clr;
std::tuple<int,int,int> Q[M];
int fa[N],siz[N],cnt,p,r;

int fnd(int i){ return i == fa[i] ? i : fa[i] = fnd(fa[i]); }

int Fnd(int i){ return i == fa[i] ? i : Fnd(fa[i]); }

int main(){
	scanf("%d%d%d",&n,&k,&m);
	B = n / sqrt(m);
	rep(i,1,n) bl[i] = (i - 1) / B + 1;
	rep(i,1,m){
		int u,v;
		scanf("%d%d",&u,&v);
		E[u].push_back(v);
		E[v].push_back(u);
	}
	scanf("%d",&q);
	std::iota(fa + 1,fa + n + 1,1);
	rep(i,1,q){
		int l,r;
		scanf("%d%d",&l,&r);
		Q[i] = std::make_tuple(l,r,i);
		if(bl[l] == bl[r]){
			ans[i] = r - l + 1;
			rep(u,l,r) for(int v : E[u])
				if(l <= v && v < u){
					int fu = fnd(u),fv = fnd(v);
					if(fu == fv) continue;
					fa[fu] = fv; --ans[i];
				}
			std::iota(fa + l,fa + r + 1,l);
		}
	}
	auto cmp = [](auto a,auto b){
		auto[l1,r1,i1] = a; auto[l2,r2,i2] = b;
		return bl[l1] == bl[l2] ? r1 < r2 : l1 < l2;
	};
	std::sort(Q + 1,Q + q + 1,cmp);
	rep(i,1,q){
		auto[L,R,id] = Q[i];
		auto[L_,R_,id_] = Q[i - 1];
		if(bl[L] != bl[L_]){
			p = r = ((bl[L] == bl[n]) ? n : bl[L] * B);
			cnt = 0; std::iota(fa + 1,fa + n + 1,1);
		}
		if(bl[L] == bl[R]) continue;
		while(r < R){
			++cnt,++r;
			for(int j : E[r])
			if(p < j && j < r){
				int fr = fnd(r),fj = fnd(j);
				if(fr == fj) continue;
				siz[fr] > siz[fj] ? fa[fj] = fr : fa[fr] = fj; --cnt;
			}
		}
		int sum = p - L + 1;
		Rep(l,p,L) for(int j : E[l])
			if(l < j && j <= R){
				int fl = Fnd(l),fj = Fnd(j);
				if(fl == fj) continue;
				clr.emplace_back(fj,fa[fj],siz[fj]);
				clr.emplace_back(fl,fa[fl],siz[fl]);
				siz[fl] > siz[fj] ? fa[fj] = fl : fa[fl] = fj; --sum;
			}
		ans[id] = sum + cnt;
		std::ranges::reverse(clr);
		for(auto[p,f,s] : clr) fa[p] = f,siz[p] = s;
		clr.clear(); 
	}
	rep(i,1,q) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2022-08-05 21:12  One_Zzz  阅读(138)  评论(0)    收藏  举报