Codeforces 1375H Set Merging (分块)

题目链接

https://codeforces.com/contest/1375/problem/H

题解

首先注意到 \(2.2\times 10^6\approx 2n\sqrt q\),因此想到分块。
考虑对值域进行分块,每块内值域连续,位置保持相对不变,大小为 \(B\),分成 \(\frac{n}{B}\) 块。
那么我们可以对每个块内的 \(\frac{B(B+1)}{2}\) 个区间中的每个合并出一个集合,然后对于每组询问在每个块内求出对应的区间,并将每个块内的集合合并到一起。
后一部分显然需要 \(\frac{nq}{B}\) 次操作,考虑前一部分怎么做。

考虑对值域进行分治,每次处理一个值域区间对应的位置,那么对于当前值域区间的一个位置区间,可以直接由两个子值域区间的对应位置区间合并得到。
总合并次数的递归式约为 \(T(n)=2T(\frac{n}{2})+\frac{n^2}{2}\),得 \(T(n)\approx n^2\). 即每个块内的预处理需要 \(B^2\) 次操作,总共就是 \(nB\) 次。

综合两部分,总共操作次数是 \(nB+\frac{nq}{B}\),取 \(B=\sqrt q\) 则可以做到总次数为 \(2n\sqrt q\).

代码

#include
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reversed_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
#define pii pair
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int mxN = 1<<12;
const int mxM = 1<<16;
const int mxSIZ = 2.2e6;
const int mxB = 1<<8;
int a[mxN+3],ai[mxN+3];
int n,m,siz,B;
int ans[mxM+3];
vector way;

int merge(int u,int v) {if(!u||!v) {return u+v;} way.push_back(mkpr(u,v)); return ++siz;}

int f[mxB*2+3][mxB+3][mxB+3];
struct Block
{
	int len,al,ar; int p[mxN+3];
	void solve(int u,int l,int r,vector vec)
	{
		if(l==r) {f[u][1][1] = ai[vec[1]]; return;}
		int mid = (l+r)>>1; vector vecl(1),vecr(1);
		for(int i=1; imid),i++)
		{
			for(int j=i-1,yl=xl-1,yr=xr-1; jmid))
			{
				if(j==i-1) continue;
				f[u][i][j] = merge(f[u<<1][xl][yl],f[u<<1|1][xr][yr]);
			}
		}
	}
	int id[mxN+3],qwq[mxB+3][mxB+3];
	void build()
	{
		vector vec(1);
		for(int i=1; i<=n; i++) {if(a[i]>=al&&a[i]<=ar) {vec.push_back(a[i]); id[i] = id[i-1]+1;} else {id[i] = id[i-1];}}
		solve(1,al,ar,vec);
		for(int i=1; i

你可能感兴趣的:(Codeforces 1375H Set Merging (分块))