bitset 求解高维偏序

发布时间 2023-09-22 21:48:32作者: xx019

菜,题简单,trick 蠢,求别骂。

记录今天做题的时候遇到的一个小 trick。

先看一道题:P3810 【模板】三维偏序(陌上花开)

平凡的三维偏序板子,相信大家都会用 CDQ/树套树/K-D tree 之类的优秀做法秒了吧!

然后看这个题:求五维偏序,\(n\le 3\times 10^4\),保证每一维这 \(n\) 个数都是 \(n\) 的排列。

我会套三层 CDQ 做到 \(\mathcal{O}(n\log^4n)\)!我还会 K-D tree 做到 \(\mathcal{O}(n^{\frac{7}{4}})\)

然后看这个题:CF1826E. Walk the Runway

题意有点复杂,但是就是需要求 \(m\) 维偏序,\(m\le 500\)\(n\le 5000\),保证每一维都 \(\le n\)

显然 CDQ 并不可取,如果直接暴力也是 \(\mathcal{O}(n^2m)\) 的,K-D tree 应该也不行(我不会)。怎么办?这里介绍一种使用 bitset 的简单做法。

有一个 \(n\times n\) 的矩阵 \(A\)\(A_{i,j}\) 为 1 表示 \(j\) 每一维都严格小于 \(i\)。枚举每一维 \(j\),排一遍序可以求出在这一维上严格小于第 \(i\) 个物品的集合,记为 \(S_{i,j}\)。显然 \(A_{i,j}\) 为 1 当且仅当 \(\forall k\in[1,m]\) 都有 \(j\in S_{i,k}\),而这个过程其实就是 bitset 求并。于是我们就做到了时间复杂度 \(\mathcal{O}(mn\log n+\dfrac{mn^2}{w})\),空间复杂度 \(\mathcal{O}(\dfrac{n^2}{w})\)\(m\) 维偏序。

还可以优化吗?

考虑这道题:\(m\) 维偏序,\(n\le 5\times 10^4\)保证每一维这 \(n\) 个数都是 \(n\) 的排列,空间 64MB

空间爆了,怎么办?考虑分块优化空间。我们对于每一维进行值域上的分块,块长 \(b=\sqrt{n}\)。定义 \(f_{i,j}\) 表示第 \(i\) 维,这一维的值落在值域上 \([1,j\times b]\) 这一区间的点集,\(g_{i,j}\) 表示第 \(i\) 维值为 \(j\) 的点。显然这个可以 \(\mathcal{O}(mn\sqrt{n})\) 预处理。询问的时候依旧是整块取现成,暴力遍历散块。

这样做的话时间复杂度依旧是 \(\mathcal{O}(mn\sqrt{n}+\dfrac{mn^2}{w})\),但是空间优化到了 \(\mathcal{O}(\dfrac{mn\sqrt{n}}{w})\)(注意这道题可以这样做是因为保证每一维这 \(n\) 个数都是 \(n\) 的排列,如果只是像上一道题一样保证 \(\le n\) 则复杂度并不正确)。

附一个 CF1826E 的代码。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[25000005];
int tot,head[5005],deg[5005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot,deg[v]++;
}
int n,m,b[5005],p[5005];ll a[5005],f[5005];
int cmp(int x,int y){
	return b[x]<b[y];
}
bitset<5005>res[5005];
void solve(){
	m=read(),n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j)res[i][j]=1;
	for(int j=1;j<=m;j++){
		for(int i=1;i<=n;i++)b[i]=read(),p[i]=i;
		sort(p+1,p+n+1,cmp);bitset<5005>tmp;
		for(int i=1;i<=n;i++){
			int pos=i;while(pos<=n&&b[p[pos]]==b[p[i]])res[p[pos]]&=tmp,pos++;
			pos--;for(int k=i;k<=pos;k++)tmp[p[k]]=1;
			i=pos;
		}
	} 
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(res[i][j])add(i,j);
	queue<int>q;for(int i=1;i<=n;i++)if(!deg[i])q.push(i),f[i]=a[i];
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].v;f[v]=max(f[v],f[u]+a[v]);
			if((--deg[v])==0)q.push(v);
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++)ans=max(ans,f[i]);
	printf("%lld\n",ans);
}
signed main(){
	int T=1;
	while(T--){
		solve();
	}
	return 0;
}

reference:几道很Interesting的偏序问题 - yybbitset 求解高维偏序