CF1375H Set Merging 题解

Codeforces
Luogu

Description.

有一个长度为 \(n(n\le 2^{12})\) 排列,和 \(Q(Q\le 2^{16})\) 个要求。
初始有 \(\{\{a_1\},\{a_2\},\cdots,\{a_n\}\}\) 这个大集合。
有一种操作:

  • 选择 \(S,T\) 两个集合,满足 \(\max\{S\}\le \min\{T\}\),把 \(S\cup T\) 加入大集合。

每个要求形如:

  • 在大集合中存在 \([l,r]\) 所有数构成的集合。

在每个瞬间最多只能有 \(2.2\times 10^6\) 个集合。

Solution.

\(2^{21}\approx 2\times 10^6\),猜测最后复杂度是 \(O(2n\sqrt q)\) 的。

对于每个询问,会被拆成 \(\frac nB\) 个块,如果我们每个块所对应的子区间全都处理好就可以 \(O(\frac nB)\) 合并,总复杂度 \(O(\frac{qn}B)\)

值域分块,对于每个块内先合并出每个子区间,要求总复杂度 \(O(\frac nB\times B^2)=O(nB)\)
考虑值域分治,两个子问题很好处理,然后合并直接 \(O(n^2)\) 合并,复杂度 \(O(T)=2O(\frac T2)+O(n^2)=O(n^2)\),总复杂度 \(O(\frac nBB^2)=O(nB)\)

\(B=\sqrt Q\) 时最优,总复杂度为 \(O(2n\sqrt q)\)

Coding.

刚开始每个块里开了 \(B*B\) 的静态数组,MLE 了,以为是这个的锅,就换成 vector 了

点击查看代码
#include<bits/stdc++.h>//{{{
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
    x=0;char c=getchar(),f=0;
    for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
    for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
const int N=1<<12|5,B=1<<8,Lim=2.2e6;
int n,q,a[N],wh[N],rsx[Lim],rsy[Lim],rst,rsid[1<<16|5];
inline int mrg(int x,int y) {return !x||!y?x|y:(rsx[++rst]=x,rsy[rst]=y,rst);}
struct block
{
	int len,pos[B+5];vector<vector<int> >id;
	inline void init(int x) {len=x,id.resize(x+1);for(int i=1;i<=x;i++) id[i].resize(x+1);}
	block(int x=0) {if(x) init(1),id[1][1]=pos[1]=wh[x];}
	inline int qryid(int l,int r)
	{//这个是值域区间
		if(r<pos[1]||l>pos[len]) return 0;
		int le=lower_bound(pos+1,pos+len+1,l)-pos;
		int ri=upper_bound(pos+1,pos+len+1,r)-pos-1;
		return le>ri?0:id[le][ri];
	}
	inline block operator+(block y)
	{
		block rs;rs.init(len+y.len);//guard that *this < y
		for(int i=1;i<=len;i++) rs.pos[i]=pos[i];
		for(int i=1;i<=y.len;i++) rs.pos[i+len]=y.pos[i];
		sort(rs.pos+1,rs.pos+rs.len+1);
		for(int i=1;i<=rs.len;i++) for(int j=i;j<=rs.len;j++)
			rs.id[i][j]=mrg(qryid(rs.pos[i],rs.pos[j]),y.qryid(rs.pos[i],rs.pos[j]));
		return rs;
	}
	inline void output()
	{
		puts("-----------------------------------------");
		printf("seg : %d\nthe val : ",len);
		for(int i=1;i<=len;i++) printf("%d%c",pos[i],i==len?'\n':' ');
		for(int i=1;i<=len;i++) for(int j=i;j<=len;j++) printf("%d%c",id[i][j],j==len?'\n':' ');
		puts("-----------------------------------------");
	}
}A[N/B+5];
inline block solve(int l,int r)
{//值域区间
	int md=(l+r)>>1;if(l==r) return block(l);
	return solve(l,md)+solve(md+1,r);
}
int main()
{
	read(n,q),rst=n;for(int i=1;i<=n;i++) read(a[i]),wh[a[i]]=i;
	for(int i=0;i<=(n-1)/B;i++) A[i]=solve(i*B+1,min(i*B+B,n));
	for(int k=1,l,r;k<=q;k++)
	{
		read(l,r),rsid[k]=0;
		for(int i=0;i<=(n-1)/B;i++) rsid[k]=mrg(rsid[k],A[i].qryid(l,r));
	}
	printf("%d\n",rst);for(int i=n+1;i<=rst;i++) printf("%d %d\n",rsx[i],rsy[i]);
	for(int i=1;i<=q;i++) printf("%d%c",rsid[i],i==q?'\n':' ');
	return 0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15416440.html